Command refactor, fixes, improvements
This commit is contained in:
53
src/Core/FileTime.Core/Command/CommandBase.cs
Normal file
53
src/Core/FileTime.Core/Command/CommandBase.cs
Normal file
@@ -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<string> _canRunMessages = new();
|
||||||
|
public Dictionary<AbsolutePath, List<OperationProgress>> OperationStatuses { get; set; } = new();
|
||||||
|
protected OperationProgress? CurrentOperationProgress { get; set; }
|
||||||
|
public virtual string DisplayLabel { get; protected set; }
|
||||||
|
public virtual IReadOnlyList<string> 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<CanCommandRun> CanRun(PointInTime startPoint);
|
||||||
|
public abstract Task<PointInTime> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/Core/FileTime.Core/Command/Copy/CalculateOperation.cs
Normal file
45
src/Core/FileTime.Core/Command/Copy/CalculateOperation.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
|
||||||
|
namespace FileTime.Core.Command.Copy
|
||||||
|
{
|
||||||
|
public class CalculateOperation : ICopyOperation
|
||||||
|
{
|
||||||
|
private readonly Dictionary<AbsolutePath, List<OperationProgress>> _operationStatuses;
|
||||||
|
public IReadOnlyDictionary<AbsolutePath, List<OperationProgress>> OperationStatuses { get; }
|
||||||
|
|
||||||
|
public CalculateOperation()
|
||||||
|
{
|
||||||
|
_operationStatuses = new Dictionary<AbsolutePath, List<OperationProgress>>();
|
||||||
|
OperationStatuses = new ReadOnlyDictionary<AbsolutePath, List<OperationProgress>>(_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<OperationProgress> operationsByFolder;
|
||||||
|
if (_operationStatuses.ContainsKey(parentPath))
|
||||||
|
{
|
||||||
|
operationsByFolder = _operationStatuses[parentPath];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
operationsByFolder = new List<OperationProgress>();
|
||||||
|
_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
119
src/Core/FileTime.Core/Command/Copy/CopyCommand.cs
Normal file
119
src/Core/FileTime.Core/Command/Copy/CopyCommand.cs
Normal file
@@ -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<AbsolutePath> Sources { get; } = new List<AbsolutePath>();
|
||||||
|
|
||||||
|
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<InputElement> Inputs { get; } = new();
|
||||||
|
public List<object>? InputResults { get; set; }
|
||||||
|
|
||||||
|
public CopyCommand()
|
||||||
|
{
|
||||||
|
DisplayLabel = "Copy";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<PointInTime> 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<AbsolutePath, AbsolutePath, OperationProgress?, CopyCommandContext, Task> 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<AbsolutePath, List<OperationProgress>>(calculateOperation.OperationStatuses);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task TraverseTree(
|
||||||
|
IEnumerable<AbsolutePath> 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<CanCommandRun> CanRun(PointInTime startPoint)
|
||||||
|
{
|
||||||
|
//TODO: implement
|
||||||
|
return Task.FromResult(CanCommandRun.True);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace FileTime.Core.Command
|
namespace FileTime.Core.Command.Copy
|
||||||
{
|
{
|
||||||
public class CopyCommandContext
|
public class CopyCommandContext
|
||||||
{
|
{
|
||||||
44
src/Core/FileTime.Core/Command/Copy/CopyOperation.cs
Normal file
44
src/Core/FileTime.Core/Command/Copy/CopyOperation.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using FileTime.Core.Models;
|
||||||
|
|
||||||
|
namespace FileTime.Core.Command.Copy
|
||||||
|
{
|
||||||
|
public class CopyOperation : ICopyOperation
|
||||||
|
{
|
||||||
|
private readonly Func<AbsolutePath, AbsolutePath, OperationProgress?, CopyCommandContext, Task> _copy;
|
||||||
|
private readonly CopyCommand _copyCommand;
|
||||||
|
|
||||||
|
public CopyOperation(Func<AbsolutePath, AbsolutePath, OperationProgress?, CopyCommandContext, Task> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/Core/FileTime.Core/Command/Copy/ICopyOperation.cs
Normal file
11
src/Core/FileTime.Core/Command/Copy/ICopyOperation.cs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/Core/FileTime.Core/Command/Copy/SimulateOperation.cs
Normal file
39
src/Core/FileTime.Core/Command/Copy/SimulateOperation.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
|
namespace FileTime.Core.Command.Copy
|
||||||
|
{
|
||||||
|
public class SimulateOperation : ICopyOperation
|
||||||
|
{
|
||||||
|
private readonly List<Difference> _newDiffs;
|
||||||
|
public IReadOnlyList<Difference> NewDiffs { get; }
|
||||||
|
|
||||||
|
public SimulateOperation()
|
||||||
|
{
|
||||||
|
_newDiffs = new List<Difference>();
|
||||||
|
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!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<AbsolutePath, AbsolutePath, OperationProgress?, CopyCommandContext, Task>? _copyOperation;
|
|
||||||
private Dictionary<AbsolutePath, List<OperationProgress>> _operationStatuses = new();
|
|
||||||
private Func<IContainer, string, Task<IContainer>>? _createContainer;
|
|
||||||
private Func<AbsolutePath, Task>? _containerCopyDone;
|
|
||||||
private OperationProgress? _currentOperationProgress;
|
|
||||||
|
|
||||||
public IList<AbsolutePath> Sources { get; } = new List<AbsolutePath>();
|
|
||||||
|
|
||||||
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<string> CanRunMessages { get; } = new List<string>().AsReadOnly();
|
|
||||||
public bool TargetIsContainer => true;
|
|
||||||
public List<InputElement> Inputs { get; } = new();
|
|
||||||
public List<object>? 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<PointInTime> 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<Difference>();
|
|
||||||
|
|
||||||
_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<AbsolutePath, AbsolutePath, OperationProgress?, CopyCommandContext, Task> 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<AbsolutePath, List<OperationProgress>>();
|
|
||||||
|
|
||||||
_copyOperation = async (from, to, _, _) =>
|
|
||||||
{
|
|
||||||
var parentPath = to.GetParent();
|
|
||||||
List<OperationProgress> operationsByFolder;
|
|
||||||
if (operationStatuses.ContainsKey(parentPath))
|
|
||||||
{
|
|
||||||
operationsByFolder = operationStatuses[parentPath];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var resolvedFrom = await from.ResolveAsync();
|
|
||||||
operationsByFolder = new List<OperationProgress>();
|
|
||||||
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<AbsolutePath> 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<CanCommandRun> CanRun(PointInTime startPoint)
|
|
||||||
{
|
|
||||||
//TODO: implement
|
|
||||||
return Task.FromResult(CanCommandRun.True);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ using AsyncEvent;
|
|||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
namespace FileTime.Core.Command
|
namespace FileTime.Core.Command.CreateContainer
|
||||||
{
|
{
|
||||||
public class CreateContainerCommand : IExecutableCommand
|
public class CreateContainerCommand : IExecutableCommand
|
||||||
{
|
{
|
||||||
@@ -16,7 +16,6 @@ namespace FileTime.Core.Command
|
|||||||
public string DisplayLabel { get; }
|
public string DisplayLabel { get; }
|
||||||
public IReadOnlyList<string> CanRunMessages { get; } = new List<string>().AsReadOnly();
|
public IReadOnlyList<string> CanRunMessages { get; } = new List<string>().AsReadOnly();
|
||||||
|
|
||||||
|
|
||||||
public CreateContainerCommand(AbsolutePath container, string newContainerName)
|
public CreateContainerCommand(AbsolutePath container, string newContainerName)
|
||||||
{
|
{
|
||||||
Container = container;
|
Container = container;
|
||||||
@@ -2,7 +2,7 @@ using AsyncEvent;
|
|||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
namespace FileTime.Core.Command
|
namespace FileTime.Core.Command.CreateElement
|
||||||
{
|
{
|
||||||
public class CreateElementCommand : IExecutableCommand
|
public class CreateElementCommand : IExecutableCommand
|
||||||
{
|
{
|
||||||
@@ -2,7 +2,7 @@ using AsyncEvent;
|
|||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
namespace FileTime.Core.Command
|
namespace FileTime.Core.Command.Delete
|
||||||
{
|
{
|
||||||
public class DeleteCommand : IExecutableCommand
|
public class DeleteCommand : IExecutableCommand
|
||||||
{
|
{
|
||||||
@@ -64,6 +64,11 @@ namespace FileTime.Core.Command
|
|||||||
{
|
{
|
||||||
await TraverseTree((await item.ResolveAsync())!);
|
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)
|
private async Task TraverseTree(IItem item)
|
||||||
@@ -3,7 +3,7 @@ using FileTime.Core.Interactions;
|
|||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
namespace FileTime.Core.Command
|
namespace FileTime.Core.Command.Move
|
||||||
{
|
{
|
||||||
public class MoveCommand : ITransportationCommand
|
public class MoveCommand : ITransportationCommand
|
||||||
{
|
{
|
||||||
@@ -2,7 +2,7 @@ using AsyncEvent;
|
|||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
namespace FileTime.Core.Command
|
namespace FileTime.Core.Command.Rename
|
||||||
{
|
{
|
||||||
public class RenameCommand : IExecutableCommand
|
public class RenameCommand : IExecutableCommand
|
||||||
{
|
{
|
||||||
@@ -27,7 +27,7 @@ namespace FileTime.Core.Command
|
|||||||
if (itemToRename != null)
|
if (itemToRename != null)
|
||||||
{
|
{
|
||||||
await itemToRename.Rename(Target);
|
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()!));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using FileTime.Core.Command;
|
using FileTime.Core.Command;
|
||||||
|
using FileTime.Core.Command.Copy;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
|
|||||||
@@ -61,42 +61,51 @@ namespace FileTime.Core.Components
|
|||||||
{
|
{
|
||||||
if (_currentlySelecting) return false;
|
if (_currentlySelecting) return false;
|
||||||
|
|
||||||
IItem? itemToSelect = null;
|
try
|
||||||
if (value != null)
|
|
||||||
{
|
{
|
||||||
itemToSelect = (await _currentLocation.GetItems(token))?.FirstOrDefault(i =>
|
_currentlySelecting = true;
|
||||||
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;
|
IItem? itemToSelect = null;
|
||||||
lock (_guardSetCurrentSelectedItemCTS)
|
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;
|
_currentlySelecting = 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;
|
|
||||||
}
|
}
|
||||||
public async Task<IItem?> GetItemByLastPath(IContainer? container = null)
|
public async Task<IItem?> GetItemByLastPath(IContainer? container = null)
|
||||||
{
|
{
|
||||||
@@ -211,30 +220,35 @@ namespace FileTime.Core.Components
|
|||||||
{
|
{
|
||||||
if (token.IsCancellationRequested) return;
|
if (token.IsCancellationRequested) return;
|
||||||
|
|
||||||
_currentlySelecting = true;
|
|
||||||
if (AutoRefresh && currentLocation != null)
|
|
||||||
{
|
|
||||||
await currentLocation.RefreshAsync(token);
|
|
||||||
if (token.IsCancellationRequested) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IItem? newSelectedItem = null;
|
IItem? newSelectedItem = null;
|
||||||
foreach (var item in currentPossibleItems)
|
try
|
||||||
{
|
{
|
||||||
if (currentLocationItems.FirstOrDefault(i => i.Name == item.Name) is var possibleNewSelectedItem
|
_currentlySelecting = true;
|
||||||
&& possibleNewSelectedItem is not null)
|
if (AutoRefresh && currentLocation != null)
|
||||||
{
|
{
|
||||||
newSelectedItem = possibleNewSelectedItem;
|
await currentLocation.RefreshAsync(token);
|
||||||
break;
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
if (newSelectedItem != null)
|
|
||||||
{
|
{
|
||||||
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);
|
await SetCurrentSelectedItem(newSelectedItem ?? (currentLocationItems.Count > 0 ? currentLocationItems[0] : null), token: token);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace FileTime.Core.Models
|
|||||||
Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default);
|
Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default);
|
||||||
|
|
||||||
Task RefreshAsync(CancellationToken token = default);
|
Task RefreshAsync(CancellationToken token = default);
|
||||||
async Task<IItem?> GetByPath(string path, bool acceptDeepestMatch = false)
|
public async Task<IItem?> GetByPath(string path, bool acceptDeepestMatch = false)
|
||||||
{
|
{
|
||||||
if (path == null) return this;
|
if (path == null) return this;
|
||||||
var paths = path.Split(Constants.SeparatorChar);
|
var paths = path.Split(Constants.SeparatorChar);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ namespace FileTime.Core.Models
|
|||||||
string? NativePath { get; }
|
string? NativePath { get; }
|
||||||
bool IsHidden { get; }
|
bool IsHidden { get; }
|
||||||
bool IsDestroyed { get; }
|
bool IsDestroyed { get; }
|
||||||
|
bool IsExists { get; }
|
||||||
SupportsDelete CanDelete { get; }
|
SupportsDelete CanDelete { get; }
|
||||||
bool CanRename { get; }
|
bool CanRename { get; }
|
||||||
IContentProvider Provider { get; }
|
IContentProvider Provider { get; }
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ namespace FileTime.Core.Models
|
|||||||
public AsyncEventHandler Refreshed { get; }
|
public AsyncEventHandler Refreshed { get; }
|
||||||
|
|
||||||
public bool IsDestroyed => BaseContainer.IsDestroyed;
|
public bool IsDestroyed => BaseContainer.IsDestroyed;
|
||||||
|
public bool IsExists => BaseContainer.IsExists;
|
||||||
|
|
||||||
private void RefreshAddBase(Func<object?, AsyncEventArgs, CancellationToken, Task> handler)
|
private void RefreshAddBase(Func<object?, AsyncEventArgs, CancellationToken, Task> handler)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,12 +37,14 @@ namespace FileTime.Core.Providers
|
|||||||
|
|
||||||
IContentProvider IItem.Provider => Provider;
|
IContentProvider IItem.Provider => Provider;
|
||||||
|
|
||||||
|
public abstract bool IsExists { get; }
|
||||||
|
|
||||||
protected AbstractContainer(TProvider provider, IContainer parent, string name)
|
protected AbstractContainer(TProvider provider, IContainer parent, string name)
|
||||||
{
|
{
|
||||||
_parent = parent;
|
_parent = parent;
|
||||||
Provider = provider;
|
Provider = provider;
|
||||||
Name = name;
|
Name = name;
|
||||||
FullName = (parent?.FullName ?? Name) + Constants.SeparatorChar + Name;
|
FullName = parent.FullName == null ? Name : parent.FullName + Constants.SeparatorChar + Name;
|
||||||
Exceptions = _exceptions.AsReadOnly();
|
Exceptions = _exceptions.AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ namespace FileTime.Core.Providers
|
|||||||
public bool SupportsDirectoryLevelSoftDelete => false;
|
public bool SupportsDirectoryLevelSoftDelete => false;
|
||||||
|
|
||||||
public bool IsDestroyed => false;
|
public bool IsDestroyed => false;
|
||||||
|
public bool IsExists => true;
|
||||||
|
|
||||||
public TopContainer(IEnumerable<IContentProvider> contentProviders)
|
public TopContainer(IEnumerable<IContentProvider> contentProviders)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ namespace FileTime.Core.Timeline
|
|||||||
|
|
||||||
//FIXME: currently this can be different of the real items NativePath, should be fixed
|
//FIXME: currently this can be different of the real items NativePath, should be fixed
|
||||||
public string? NativePath => FullName;
|
public string? NativePath => FullName;
|
||||||
|
public bool IsExists => true;
|
||||||
|
|
||||||
public TimeContainer(string name, IContainer parent, IContentProvider contentProvider, IContentProvider virtualContentProvider, PointInTime pointInTime)
|
public TimeContainer(string name, IContainer parent, IContentProvider contentProvider, IContentProvider virtualContentProvider, PointInTime pointInTime)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ namespace FileTime.Core.Timeline
|
|||||||
public IContentProvider VirtualProvider { get; }
|
public IContentProvider VirtualProvider { get; }
|
||||||
|
|
||||||
public bool IsDestroyed { get; private set; }
|
public bool IsDestroyed { get; private set; }
|
||||||
|
public bool IsExists => true;
|
||||||
|
|
||||||
public Task Delete(bool hardDelete = false) => Task.CompletedTask;
|
public Task Delete(bool hardDelete = false) => Task.CompletedTask;
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ namespace FileTime.Core.Timeline
|
|||||||
public bool SupportsContentStreams => false;
|
public bool SupportsContentStreams => false;
|
||||||
|
|
||||||
public string Protocol => "time2://";
|
public string Protocol => "time2://";
|
||||||
|
public bool IsExists => true;
|
||||||
|
|
||||||
public TimeProvider(PointInTime pointInTime)
|
public TimeProvider(PointInTime pointInTime)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ namespace FileTime.Avalonia
|
|||||||
.BuildServiceProvider()
|
.BuildServiceProvider()
|
||||||
.InitSerilog();
|
.InitSerilog();
|
||||||
|
|
||||||
var _logger = ServiceProvider.GetService<ILogger<App>>();
|
var logger = ServiceProvider.GetService<ILogger<App>>();
|
||||||
_logger?.LogInformation("App initialization completed.");
|
logger?.LogInformation("App initialization completed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ using System.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using FileTime.App.Core.Tab;
|
using FileTime.App.Core.Tab;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
namespace FileTime.Avalonia.Application
|
namespace FileTime.Avalonia.Application
|
||||||
{
|
{
|
||||||
@@ -18,6 +19,7 @@ namespace FileTime.Avalonia.Application
|
|||||||
[Inject(typeof(ItemNameConverterService))]
|
[Inject(typeof(ItemNameConverterService))]
|
||||||
[Inject(typeof(LocalContentProvider))]
|
[Inject(typeof(LocalContentProvider))]
|
||||||
[Inject(typeof(Tab))]
|
[Inject(typeof(Tab))]
|
||||||
|
[Inject(typeof(TimeRunner), propertyName: "_timeRunner")]
|
||||||
public partial class TabContainer : INewItemProcessor
|
public partial class TabContainer : INewItemProcessor
|
||||||
{
|
{
|
||||||
private bool _updateFromCode;
|
private bool _updateFromCode;
|
||||||
@@ -77,6 +79,21 @@ namespace FileTime.Avalonia.Application
|
|||||||
partial void OnInitialize()
|
partial void OnInitialize()
|
||||||
{
|
{
|
||||||
_tabState = new TabState(Tab);
|
_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)
|
public async Task Init(int tabNumber)
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ using FileTime.Avalonia.Misc;
|
|||||||
using FileTime.Avalonia.Models;
|
using FileTime.Avalonia.Models;
|
||||||
using FileTime.Avalonia.ViewModels;
|
using FileTime.Avalonia.ViewModels;
|
||||||
using FileTime.Core.Command;
|
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.Components;
|
||||||
using FileTime.Core.Interactions;
|
using FileTime.Core.Interactions;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
@@ -229,7 +235,7 @@ namespace FileTime.Avalonia.Services
|
|||||||
var newTab = new Tab();
|
var newTab = new Tab();
|
||||||
await newTab.Init(newContainer);
|
await newTab.Init(newContainer);
|
||||||
|
|
||||||
tabContainer = new TabContainer(newTab, _localContentProvider, _itemNameConverterService);
|
tabContainer = new TabContainer(_timeRunner, newTab, _localContentProvider, _itemNameConverterService);
|
||||||
await tabContainer.Init(number);
|
await tabContainer.Init(number);
|
||||||
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
|
||||||
using System.Text;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
@@ -13,6 +11,7 @@ using FileTime.Core.Providers;
|
|||||||
using FileTime.Providers.Local;
|
using FileTime.Providers.Local;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
namespace FileTime.Avalonia.Services
|
namespace FileTime.Avalonia.Services
|
||||||
{
|
{
|
||||||
@@ -25,13 +24,15 @@ namespace FileTime.Avalonia.Services
|
|||||||
private readonly IEnumerable<IContentProvider> _contentProviders;
|
private readonly IEnumerable<IContentProvider> _contentProviders;
|
||||||
private readonly LocalContentProvider _localContentProvider;
|
private readonly LocalContentProvider _localContentProvider;
|
||||||
private readonly ILogger<StatePersistenceService> _logger;
|
private readonly ILogger<StatePersistenceService> _logger;
|
||||||
|
private readonly TimeRunner _timeRunner;
|
||||||
|
|
||||||
public StatePersistenceService(
|
public StatePersistenceService(
|
||||||
AppState appState,
|
AppState appState,
|
||||||
ItemNameConverterService itemNameConverterService,
|
ItemNameConverterService itemNameConverterService,
|
||||||
IEnumerable<IContentProvider> contentProviders,
|
IEnumerable<IContentProvider> contentProviders,
|
||||||
LocalContentProvider localContentProvider,
|
LocalContentProvider localContentProvider,
|
||||||
ILogger<StatePersistenceService> logger)
|
ILogger<StatePersistenceService> logger,
|
||||||
|
TimeRunner timeRunner)
|
||||||
{
|
{
|
||||||
_appState = appState;
|
_appState = appState;
|
||||||
_itemNameConverterService = itemNameConverterService;
|
_itemNameConverterService = itemNameConverterService;
|
||||||
@@ -45,6 +46,7 @@ namespace FileTime.Avalonia.Services
|
|||||||
PropertyNameCaseInsensitive = true,
|
PropertyNameCaseInsensitive = true,
|
||||||
WriteIndented = true
|
WriteIndented = true
|
||||||
};
|
};
|
||||||
|
this._timeRunner = timeRunner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LoadStatesAsync()
|
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);
|
await newTabContainer.Init(tab.Number);
|
||||||
_appState.Tabs.Add(newTabContainer);
|
_appState.Tabs.Add(newTabContainer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,10 +154,10 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
{
|
{
|
||||||
_isRefreshing = true;
|
_isRefreshing = true;
|
||||||
|
|
||||||
List<ContainerViewModel> newContainers = new List<ContainerViewModel>();
|
List<ContainerViewModel> newContainers = new();
|
||||||
List<ElementViewModel> newElements = new List<ElementViewModel>();
|
List<ElementViewModel> newElements = new();
|
||||||
|
|
||||||
if (await _container.GetContainers() is IReadOnlyList<IContainer> containers)
|
if (await _container.GetContainers(token) is IReadOnlyList<IContainer> containers)
|
||||||
{
|
{
|
||||||
foreach (var container in containers)
|
foreach (var container in containers)
|
||||||
{
|
{
|
||||||
@@ -165,7 +165,7 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await _container.GetElements() is IReadOnlyList<IElement> elements)
|
if (await _container.GetElements(token) is IReadOnlyList<IElement> elements)
|
||||||
{
|
{
|
||||||
foreach (var element in elements)
|
foreach (var element in elements)
|
||||||
{
|
{
|
||||||
@@ -216,7 +216,7 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
_exceptions.Add(e);
|
_exceptions.Add(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _newItemProcessor.UpdateMarkedItems(this);
|
await _newItemProcessor.UpdateMarkedItems(this, CancellationToken.None);
|
||||||
|
|
||||||
_isRefreshing = false;
|
_isRefreshing = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
var tab = new Tab();
|
var tab = new Tab();
|
||||||
await tab.Init(LocalContentProvider);
|
await tab.Init(LocalContentProvider);
|
||||||
|
|
||||||
var tabContainer = new TabContainer(tab, LocalContentProvider, ItemNameConverterService);
|
var tabContainer = new TabContainer(_timeRunner, tab, LocalContentProvider, ItemNameConverterService);
|
||||||
await tabContainer.Init(1);
|
await tabContainer.Init(1);
|
||||||
tabContainer.IsSelected = true;
|
tabContainer.IsSelected = true;
|
||||||
AppState.Tabs.Add(tabContainer);
|
AppState.Tabs.Add(tabContainer);
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ namespace FileTime.Avalonia.Views
|
|||||||
|
|
||||||
private void InputText_AttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
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();
|
inputText.Focus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ namespace FileTime.Providers.Local
|
|||||||
|
|
||||||
public bool IsDestroyed => false;
|
public bool IsDestroyed => false;
|
||||||
public bool SupportsContentStreams => true;
|
public bool SupportsContentStreams => true;
|
||||||
|
public bool IsExists => true;
|
||||||
|
|
||||||
public LocalContentProvider(ILogger<LocalContentProvider> logger)
|
public LocalContentProvider(ILogger<LocalContentProvider> logger)
|
||||||
{
|
{
|
||||||
@@ -49,8 +50,6 @@ namespace FileTime.Providers.Local
|
|||||||
? new DirectoryInfo("/").GetDirectories()
|
? new DirectoryInfo("/").GetDirectories()
|
||||||
: Environment.GetLogicalDrives().Select(d => new DirectoryInfo(d));
|
: 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();
|
_rootContainers = rootDirectories.Select(d => new LocalFolder(d, this, this)).OrderBy(d => d.Name).ToList().AsReadOnly();
|
||||||
_items = _rootContainers.Cast<IItem>().ToList().AsReadOnly();
|
_items = _rootContainers.Cast<IItem>().ToList().AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ namespace FileTime.Providers.Local
|
|||||||
|
|
||||||
public string FullName { get; }
|
public string FullName { get; }
|
||||||
public string? NativePath => File.FullName;
|
public string? NativePath => File.FullName;
|
||||||
|
public bool IsExists => File.Exists;
|
||||||
|
|
||||||
public IContentProvider Provider { get; }
|
public IContentProvider Provider { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ using FileTime.Providers.Local.Interop;
|
|||||||
|
|
||||||
namespace FileTime.Providers.Local
|
namespace FileTime.Providers.Local
|
||||||
{
|
{
|
||||||
public class LocalFolder : AbstractContainer<LocalContentProvider>
|
public class LocalFolder : AbstractContainer<LocalContentProvider>, IContainer
|
||||||
{
|
{
|
||||||
public DirectoryInfo Directory { get; }
|
public DirectoryInfo Directory { get; }
|
||||||
|
|
||||||
public string Attributes => GetAttributes();
|
public string Attributes => GetAttributes();
|
||||||
|
|
||||||
public DateTime CreatedAt => Directory.CreationTime;
|
public DateTime CreatedAt => Directory.CreationTime;
|
||||||
|
public override bool IsExists => Directory.Exists;
|
||||||
|
|
||||||
public LocalFolder(DirectoryInfo directory, LocalContentProvider contentProvider, IContainer parent)
|
public LocalFolder(DirectoryInfo directory, LocalContentProvider contentProvider, IContainer parent)
|
||||||
: base(contentProvider, parent, directory.Name.TrimEnd(Path.DirectorySeparatorChar))
|
: base(contentProvider, parent, directory.Name.TrimEnd(Path.DirectorySeparatorChar))
|
||||||
@@ -52,7 +53,7 @@ namespace FileTime.Providers.Local
|
|||||||
return Task.FromResult(Enumerable.Empty<IItem>());
|
return Task.FromResult(Enumerable.Empty<IItem>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IItem?> GetByPath(string path, bool acceptDeepestMatch = false)
|
async Task<IItem?> IContainer.GetByPath(string path, bool acceptDeepestMatch)
|
||||||
{
|
{
|
||||||
var paths = path.Split(Constants.SeparatorChar);
|
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))!;
|
return (await GetElements())!.FirstOrDefault(e => Provider.NormalizePath(e.Name) == Provider.NormalizePath(name))!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<bool> IsExistsAsync(string name) => (await GetItems())?.Any(i => Provider.NormalizePath(i.Name) == Provider.NormalizePath(name)) ?? false;
|
public override async Task<bool> IsExistsAsync(string name) => (await GetItems())?.Any(i => i.IsExists && Provider.NormalizePath(i.Name) == Provider.NormalizePath(name)) ?? false;
|
||||||
|
|
||||||
public override Task Delete(bool hardDelete = false)
|
public override Task Delete(bool hardDelete = false)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ namespace FileTime.Providers.Sftp
|
|||||||
public IContentProvider Provider => this;
|
public IContentProvider Provider => this;
|
||||||
|
|
||||||
public string Protocol => "sftp://";
|
public string Protocol => "sftp://";
|
||||||
|
public bool IsExists => true;
|
||||||
|
|
||||||
public SftpContentProvider(IInputInterface inputInterface, ILogger<SftpContentProvider> logger)
|
public SftpContentProvider(IInputInterface inputInterface, ILogger<SftpContentProvider> logger)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ namespace FileTime.Providers.Sftp
|
|||||||
|
|
||||||
public IContentProvider Provider => throw new NotImplementedException();
|
public IContentProvider Provider => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public bool IsExists => throw new NotImplementedException();
|
||||||
|
|
||||||
public Task Delete(bool hardDelete = false)
|
public Task Delete(bool hardDelete = false)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ namespace FileTime.Providers.Sftp
|
|||||||
public class SftpFolder : AbstractContainer<SftpContentProvider>
|
public class SftpFolder : AbstractContainer<SftpContentProvider>
|
||||||
{
|
{
|
||||||
private readonly SftpServer _server;
|
private readonly SftpServer _server;
|
||||||
|
public override bool IsExists => true;
|
||||||
|
|
||||||
public SftpFolder(SftpContentProvider provider, SftpServer server, IContainer parent, string path) : base(provider, parent, path)
|
public SftpFolder(SftpContentProvider provider, SftpServer server, IContainer parent, string path) : base(provider, parent, path)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ namespace FileTime.Providers.Sftp
|
|||||||
|
|
||||||
public string? Username { get; private set; }
|
public string? Username { get; private set; }
|
||||||
public string? Password { 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)
|
public SftpServer(string name, SftpContentProvider sftpContentProvider, IInputInterface inputInterface, string? username = null, string? password = null)
|
||||||
: base(sftpContentProvider, sftpContentProvider, name)
|
: base(sftpContentProvider, sftpContentProvider, name)
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
public bool IsDestroyed => false;
|
public bool IsDestroyed => false;
|
||||||
public bool SupportsContentStreams => true;
|
public bool SupportsContentStreams => true;
|
||||||
|
public bool IsExists => true;
|
||||||
|
|
||||||
public SmbContentProvider(IInputInterface inputInterface, Persistence.PersistenceService persistenceService, ILogger<SmbContentProvider> logger)
|
public SmbContentProvider(IInputInterface inputInterface, Persistence.PersistenceService persistenceService, ILogger<SmbContentProvider> logger)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ namespace FileTime.Providers.Smb
|
|||||||
public IContentProvider Provider { get; }
|
public IContentProvider Provider { get; }
|
||||||
|
|
||||||
public bool IsDestroyed { get; private set; }
|
public bool IsDestroyed { get; private set; }
|
||||||
|
//TODO: implement
|
||||||
|
public bool IsExists => true;
|
||||||
|
|
||||||
public SmbFile(string name, SmbContentProvider provider, SmbShare smbShare, IContainer parent, SmbClientContext smbClientContext)
|
public SmbFile(string name, SmbContentProvider provider, SmbShare smbShare, IContainer parent, SmbClientContext smbClientContext)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ namespace FileTime.Providers.Smb
|
|||||||
{
|
{
|
||||||
private readonly SmbClientContext _smbClientContext;
|
private readonly SmbClientContext _smbClientContext;
|
||||||
|
|
||||||
|
|
||||||
public SmbShare SmbShare { get; }
|
public SmbShare SmbShare { get; }
|
||||||
|
//TODO: implement
|
||||||
|
public override bool IsExists => true;
|
||||||
|
|
||||||
public SmbFolder(string name, SmbContentProvider contentProvider, SmbShare smbShare, IContainer parent, SmbClientContext smbClientContext)
|
public SmbFolder(string name, SmbContentProvider contentProvider, SmbShare smbShare, IContainer parent, SmbClientContext smbClientContext)
|
||||||
: base(contentProvider, parent, name)
|
: base(contentProvider, parent, name)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using SMBLibrary.Client;
|
|||||||
|
|
||||||
namespace FileTime.Providers.Smb
|
namespace FileTime.Providers.Smb
|
||||||
{
|
{
|
||||||
public class SmbServer : AbstractContainer<SmbContentProvider>
|
public class SmbServer : AbstractContainer<SmbContentProvider>, IContainer
|
||||||
{
|
{
|
||||||
internal const int MAXRETRIES = 5;
|
internal const int MAXRETRIES = 5;
|
||||||
|
|
||||||
@@ -21,6 +21,7 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
public string? Username { get; private set; }
|
public string? Username { get; private set; }
|
||||||
public string? Password { 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)
|
public SmbServer(string name, SmbContentProvider contentProvider, IInputInterface inputInterface, string? username = null, string? password = null)
|
||||||
: base(contentProvider, contentProvider, name)
|
: base(contentProvider, contentProvider, name)
|
||||||
@@ -52,7 +53,7 @@ namespace FileTime.Providers.Smb
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IItem?> GetByPath(string path, bool acceptDeepestMatch = false)
|
async Task<IItem?> IContainer.GetByPath(string path, bool acceptDeepestMatch)
|
||||||
{
|
{
|
||||||
var paths = path.Split(Constants.SeparatorChar);
|
var paths = path.Split(Constants.SeparatorChar);
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace FileTime.Providers.Smb
|
|||||||
public class SmbShare : AbstractContainer<SmbContentProvider>
|
public class SmbShare : AbstractContainer<SmbContentProvider>
|
||||||
{
|
{
|
||||||
private readonly SmbClientContext _smbClientContext;
|
private readonly SmbClientContext _smbClientContext;
|
||||||
|
public override bool IsExists => true;
|
||||||
|
|
||||||
public SmbShare(string name, SmbContentProvider contentProvider, IContainer parent, SmbClientContext smbClientContext)
|
public SmbShare(string name, SmbContentProvider contentProvider, IContainer parent, SmbClientContext smbClientContext)
|
||||||
: base(contentProvider, parent, name)
|
: base(contentProvider, parent, name)
|
||||||
|
|||||||
Reference in New Issue
Block a user