Cancel commands
This commit is contained in:
@@ -7,4 +7,5 @@ public interface ICommandTimeStateViewModel
|
|||||||
IObservable<string> DisplayLabel { get; }
|
IObservable<string> DisplayLabel { get; }
|
||||||
IObservable<string> DisplayDetailLabel { get; }
|
IObservable<string> DisplayDetailLabel { get; }
|
||||||
IObservable<bool> IsSelected { get; }
|
IObservable<bool> IsSelected { get; }
|
||||||
|
void Cancel();
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ namespace FileTime.App.Core.ViewModels.Timeline;
|
|||||||
|
|
||||||
public class CommandTimeStateViewModel : ICommandTimeStateViewModel
|
public class CommandTimeStateViewModel : ICommandTimeStateViewModel
|
||||||
{
|
{
|
||||||
|
private readonly CommandTimeState _commandTimeState;
|
||||||
public IObservable<int> TotalProgress { get; }
|
public IObservable<int> TotalProgress { get; }
|
||||||
public IObservable<int> CurrentProgress { get; }
|
public IObservable<int> CurrentProgress { get; }
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@ public class CommandTimeStateViewModel : ICommandTimeStateViewModel
|
|||||||
|
|
||||||
public CommandTimeStateViewModel(CommandTimeState commandTimeState)
|
public CommandTimeStateViewModel(CommandTimeState commandTimeState)
|
||||||
{
|
{
|
||||||
|
_commandTimeState = commandTimeState;
|
||||||
DisplayLabel = commandTimeState.Command.DisplayLabel;
|
DisplayLabel = commandTimeState.Command.DisplayLabel;
|
||||||
DisplayDetailLabel = commandTimeState.Command.DisplayDetailLabel;
|
DisplayDetailLabel = commandTimeState.Command.DisplayDetailLabel;
|
||||||
TotalProgress = commandTimeState.Command.TotalProgress;
|
TotalProgress = commandTimeState.Command.TotalProgress;
|
||||||
@@ -22,4 +24,7 @@ public class CommandTimeStateViewModel : ICommandTimeStateViewModel
|
|||||||
//TODO
|
//TODO
|
||||||
IsSelected = new BehaviorSubject<bool>(false);
|
IsSelected = new BehaviorSubject<bool>(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Cancel()
|
||||||
|
=> _commandTimeState.Command.Cancel();
|
||||||
}
|
}
|
||||||
@@ -11,4 +11,5 @@ public interface ICommand
|
|||||||
|
|
||||||
Task<CanCommandRun> CanRun(PointInTime currentTime);
|
Task<CanCommandRun> CanRun(PointInTime currentTime);
|
||||||
Task<PointInTime> SimulateCommand(PointInTime currentTime);
|
Task<PointInTime> SimulateCommand(PointInTime currentTime);
|
||||||
|
void Cancel();
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,6 @@ public interface IContentWriter : IDisposable
|
|||||||
{
|
{
|
||||||
int PreferredBufferSize { get; }
|
int PreferredBufferSize { get; }
|
||||||
|
|
||||||
Task WriteBytesAsync(byte[] data, int? index = null);
|
Task WriteBytesAsync(byte[] data, int? index = null, CancellationToken cancellationToken = default);
|
||||||
Task FlushAsync();
|
Task FlushAsync(CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
@@ -31,6 +31,7 @@ public abstract class CommandBase : ICommand
|
|||||||
|
|
||||||
public abstract Task<CanCommandRun> CanRun(PointInTime currentTime);
|
public abstract Task<CanCommandRun> CanRun(PointInTime currentTime);
|
||||||
public abstract Task<PointInTime> SimulateCommand(PointInTime currentTime);
|
public abstract Task<PointInTime> SimulateCommand(PointInTime currentTime);
|
||||||
|
public abstract void Cancel();
|
||||||
|
|
||||||
protected void SetDisplayLabel(string? displayLabel) => _displayLabel.OnNext(displayLabel ?? string.Empty);
|
protected void SetDisplayLabel(string? displayLabel) => _displayLabel.OnNext(displayLabel ?? string.Empty);
|
||||||
protected void SetDisplayDetailLabel(string? displayLabel) => _displayDetailLabel.OnNext(displayLabel ?? string.Empty);
|
protected void SetDisplayDetailLabel(string? displayLabel) => _displayDetailLabel.OnNext(displayLabel ?? string.Empty);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ public class CopyCommand : CommandBase, ITransportationCommand
|
|||||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||||
private readonly ICommandSchedulerNotifier _commandSchedulerNotifier;
|
private readonly ICommandSchedulerNotifier _commandSchedulerNotifier;
|
||||||
private readonly ILogger<CopyCommand> _logger;
|
private readonly ILogger<CopyCommand> _logger;
|
||||||
|
private readonly CancellationTokenSource _cancellationTokenSource = new();
|
||||||
|
|
||||||
private readonly List<OperationProgress> _operationProgresses = new();
|
private readonly List<OperationProgress> _operationProgresses = new();
|
||||||
private readonly BehaviorSubject<OperationProgress?> _currentOperationProgress = new(null);
|
private readonly BehaviorSubject<OperationProgress?> _currentOperationProgress = new(null);
|
||||||
@@ -94,6 +95,9 @@ public class CopyCommand : CommandBase, ITransportationCommand
|
|||||||
return currentTime.WithDifferences(simulateOperation.NewDiffs);
|
return currentTime.WithDifferences(simulateOperation.NewDiffs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Cancel()
|
||||||
|
=> _cancellationTokenSource.Cancel();
|
||||||
|
|
||||||
public async Task ExecuteAsync(CopyFunc copy)
|
public async Task ExecuteAsync(CopyFunc copy)
|
||||||
{
|
{
|
||||||
var currentTime = PointInTime.Present;
|
var currentTime = PointInTime.Present;
|
||||||
@@ -163,6 +167,8 @@ public class CopyCommand : CommandBase, ITransportationCommand
|
|||||||
{
|
{
|
||||||
foreach (var source in sources)
|
foreach (var source in sources)
|
||||||
{
|
{
|
||||||
|
if (_cancellationTokenSource.IsCancellationRequested) return;
|
||||||
|
|
||||||
var resolvedTarget = (IContainer) await target.ResolveAsync() ?? throw new Exception();
|
var resolvedTarget = (IContainer) await target.ResolveAsync() ?? throw new Exception();
|
||||||
var item = await _timelessContentProvider.GetItemByFullNameAsync(source, currentTime);
|
var item = await _timelessContentProvider.GetItemByFullNameAsync(source, currentTime);
|
||||||
|
|
||||||
@@ -188,12 +194,13 @@ public class CopyCommand : CommandBase, ITransportationCommand
|
|||||||
var currentProgress = _operationProgresses.Find(o => o.Key == element.FullName!.Path);
|
var currentProgress = _operationProgresses.Find(o => o.Key == element.FullName!.Path);
|
||||||
_currentOperationProgress.OnNext(currentProgress);
|
_currentOperationProgress.OnNext(currentProgress);
|
||||||
|
|
||||||
await copyOperation.CopyAsync(new AbsolutePath(_timelessContentProvider, element), newElementPath, new CopyCommandContext(UpdateProgress, currentProgress));
|
await copyOperation.CopyAsync(new AbsolutePath(_timelessContentProvider, element), newElementPath, new CopyCommandContext(UpdateProgress, currentProgress, _cancellationTokenSource.Token));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly object _updateProgressLock = new();
|
private readonly object _updateProgressLock = new();
|
||||||
|
|
||||||
private Task UpdateProgress()
|
private Task UpdateProgress()
|
||||||
{
|
{
|
||||||
lock (_updateProgressLock)
|
lock (_updateProgressLock)
|
||||||
|
|||||||
@@ -4,13 +4,18 @@ public class CopyCommandContext
|
|||||||
{
|
{
|
||||||
private readonly Func<Task> _updateProgress;
|
private readonly Func<Task> _updateProgress;
|
||||||
|
|
||||||
public CopyCommandContext(Func<Task> updateProgress, OperationProgress? currentProgress)
|
public CopyCommandContext(
|
||||||
|
Func<Task> updateProgress,
|
||||||
|
OperationProgress? currentProgress,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_updateProgress = updateProgress;
|
_updateProgress = updateProgress;
|
||||||
|
CancellationToken = cancellationToken;
|
||||||
CurrentProgress = currentProgress;
|
CurrentProgress = currentProgress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OperationProgress? CurrentProgress { get; }
|
public OperationProgress? CurrentProgress { get; }
|
||||||
|
public CancellationToken CancellationToken { get; }
|
||||||
|
|
||||||
public async Task UpdateProgressAsync() => await _updateProgress.Invoke();
|
public async Task UpdateProgressAsync() => await _updateProgress.Invoke();
|
||||||
}
|
}
|
||||||
@@ -23,4 +23,8 @@ public class CreateContainerCommand : CreateItemBase
|
|||||||
await itemCreator.CreateContainerAsync(resolvedParent.Provider, Parent!.GetChild(NewItemName!));
|
await itemCreator.CreateContainerAsync(resolvedParent.Provider, Parent!.GetChild(NewItemName!));
|
||||||
await _commandSchedulerNotifier.RefreshContainer(Parent);
|
await _commandSchedulerNotifier.RefreshContainer(Parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Cancel()
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@ public class CreateElementCommand : CreateItemBase
|
|||||||
private readonly ICommandSchedulerNotifier _commandSchedulerNotifier;
|
private readonly ICommandSchedulerNotifier _commandSchedulerNotifier;
|
||||||
|
|
||||||
public CreateElementCommand(
|
public CreateElementCommand(
|
||||||
ITimelessContentProvider timelessContentProvider,
|
ITimelessContentProvider timelessContentProvider,
|
||||||
IContentAccessorFactory contentAccessorFactory,
|
IContentAccessorFactory contentAccessorFactory,
|
||||||
ICommandSchedulerNotifier commandSchedulerNotifier)
|
ICommandSchedulerNotifier commandSchedulerNotifier)
|
||||||
: base(timelessContentProvider, contentAccessorFactory)
|
: base(timelessContentProvider, contentAccessorFactory)
|
||||||
@@ -23,4 +23,8 @@ public class CreateElementCommand : CreateItemBase
|
|||||||
await itemCreator.CreateElementAsync(resolvedParent.Provider, Parent!.GetChild(NewItemName!));
|
await itemCreator.CreateElementAsync(resolvedParent.Provider, Parent!.GetChild(NewItemName!));
|
||||||
await _commandSchedulerNotifier.RefreshContainer(Parent);
|
await _commandSchedulerNotifier.RefreshContainer(Parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Cancel()
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -35,6 +35,11 @@ public class DeleteCommand : CommandBase, IExecutableCommand
|
|||||||
return Task.FromResult(currentTime);
|
return Task.FromResult(currentTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Cancel()
|
||||||
|
{
|
||||||
|
//TODO: Implement
|
||||||
|
}
|
||||||
|
|
||||||
public async Task Execute()
|
public async Task Execute()
|
||||||
{
|
{
|
||||||
//Calculate
|
//Calculate
|
||||||
|
|||||||
@@ -41,6 +41,11 @@ public class MoveCommand : CommandBase, IExecutableCommand
|
|||||||
return Task.FromResult(currentTime);
|
return Task.FromResult(currentTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Cancel()
|
||||||
|
{
|
||||||
|
//TODO: Implement
|
||||||
|
}
|
||||||
|
|
||||||
public async Task Execute()
|
public async Task Execute()
|
||||||
{
|
{
|
||||||
Calculate();
|
Calculate();
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ public class StreamCopyCommandHandler : ICommandHandler
|
|||||||
|
|
||||||
public async Task CopyElement(AbsolutePath sourcePath, AbsolutePath targetPath, CopyCommandContext copyCommandContext)
|
public async Task CopyElement(AbsolutePath sourcePath, AbsolutePath targetPath, CopyCommandContext copyCommandContext)
|
||||||
{
|
{
|
||||||
|
if (copyCommandContext.CancellationToken.IsCancellationRequested) return;
|
||||||
|
|
||||||
var parent = (IContainer?) (await targetPath.GetParent()!.ResolveAsync())!;
|
var parent = (IContainer?) (await targetPath.GetParent()!.ResolveAsync())!;
|
||||||
var elementName = targetPath.Path;
|
var elementName = targetPath.Path;
|
||||||
var parentChildren = parent.Items.ToList();
|
var parentChildren = parent.Items.ToList();
|
||||||
@@ -70,11 +72,12 @@ public class StreamCopyCommandHandler : ICommandHandler
|
|||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
if (copyCommandContext.CancellationToken.IsCancellationRequested) return;
|
||||||
dataRead = await reader.ReadBytesAsync(writer.PreferredBufferSize);
|
dataRead = await reader.ReadBytesAsync(writer.PreferredBufferSize);
|
||||||
if (dataRead.Length > 0)
|
if (dataRead.Length > 0)
|
||||||
{
|
{
|
||||||
await writer.WriteBytesAsync(dataRead);
|
await writer.WriteBytesAsync(dataRead, cancellationToken: copyCommandContext.CancellationToken);
|
||||||
await writer.FlushAsync();
|
await writer.FlushAsync(copyCommandContext.CancellationToken);
|
||||||
currentProgress += dataRead.LongLength;
|
currentProgress += dataRead.LongLength;
|
||||||
if (copyCommandContext.CurrentProgress is not null)
|
if (copyCommandContext.CurrentProgress is not null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M8.27,3L3,8.27V15.73L8.27,21H15.73L21,15.73V8.27L15.73,3M8.41,7L12,10.59L15.59,7L17,8.41L13.41,12L17,15.59L15.59,17L12,13.41L8.41,17L7,15.59L10.59,12L7,8.41" /></svg>
|
||||||
|
After Width: | Height: | Size: 235 B |
@@ -343,7 +343,18 @@
|
|||||||
Padding="5"
|
Padding="5"
|
||||||
VerticalAlignment="Top">
|
VerticalAlignment="Top">
|
||||||
<Grid ColumnDefinitions="*, *" RowDefinitions="Auto, Auto">
|
<Grid ColumnDefinitions="*, *" RowDefinitions="Auto, Auto">
|
||||||
<TextBlock Grid.ColumnSpan="2" Text="{Binding DisplayLabel^}" />
|
<Grid ColumnDefinitions="*, Auto" Grid.ColumnSpan="2">
|
||||||
|
<TextBlock Text="{Binding DisplayLabel^}" />
|
||||||
|
|
||||||
|
<Button Command="{Binding Cancel}" Grid.Column="1">
|
||||||
|
<Image
|
||||||
|
Height="20"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Source="{SvgImage /Assets/material/close-octagon.svg}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Width="20" />
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public class LocalContentWriter : IContentWriter
|
|||||||
_binaryWriter = new BinaryWriter(_writerStream);
|
_binaryWriter = new BinaryWriter(_writerStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task WriteBytesAsync(byte[] data, int? index = null)
|
public Task WriteBytesAsync(byte[] data, int? index = null, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (index != null)
|
if (index != null)
|
||||||
{
|
{
|
||||||
@@ -28,7 +28,7 @@ public class LocalContentWriter : IContentWriter
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task FlushAsync()
|
public Task FlushAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
_binaryWriter.Flush();
|
_binaryWriter.Flush();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|||||||
@@ -31,16 +31,16 @@ public class RemoteContentWriter : IRemoteContentWriter
|
|||||||
|
|
||||||
public int PreferredBufferSize => 10 * 1024 * 1024;
|
public int PreferredBufferSize => 10 * 1024 * 1024;
|
||||||
|
|
||||||
public async Task WriteBytesAsync(byte[] data, int? index = null)
|
public async Task WriteBytesAsync(byte[] data, int? index = null, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (!_isRemoteWriterInitialized) await InitializeRemoteWriter(_nativePath);
|
if (!_isRemoteWriterInitialized) await InitializeRemoteWriter(_nativePath);
|
||||||
await _remoteConnection.WriteBytesAsync(_transactionId, data, index);
|
await _remoteConnection.WriteBytesAsync(_transactionId, data, index, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task FlushAsync()
|
public async Task FlushAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (!_isRemoteWriterInitialized) return;
|
if (!_isRemoteWriterInitialized) return;
|
||||||
await _remoteConnection.FlushWriterAsync(_transactionId);
|
await _remoteConnection.FlushWriterAsync(_transactionId, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task InitializeRemoteWriter(NativePath nativePath)
|
private async Task InitializeRemoteWriter(NativePath nativePath)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public interface IRemoteConnection
|
|||||||
Task DeleteItemAsync(string contentProviderId, FullName fullName);
|
Task DeleteItemAsync(string contentProviderId, FullName fullName);
|
||||||
Task MoveItemAsync(string contentProviderId, FullName fullName, FullName newPath);
|
Task MoveItemAsync(string contentProviderId, FullName fullName, FullName newPath);
|
||||||
Task InitializeRemoteWriter(string contentProviderId, string transactionId, NativePath nativePath);
|
Task InitializeRemoteWriter(string contentProviderId, string transactionId, NativePath nativePath);
|
||||||
Task WriteBytesAsync(string transactionId, byte[] data, int? index);
|
Task WriteBytesAsync(string transactionId, byte[] data, int? index, CancellationToken cancellationToken = default);
|
||||||
Task FlushWriterAsync(string transactionId);
|
Task FlushWriterAsync(string transactionId, CancellationToken cancellationToken = default);
|
||||||
Task CloseWriterAsync(string transactionId);
|
Task CloseWriterAsync(string transactionId);
|
||||||
}
|
}
|
||||||
@@ -7,8 +7,12 @@ public interface ISignalRHub
|
|||||||
Task CreateElementAsync(string contentProviderId, string fullName);
|
Task CreateElementAsync(string contentProviderId, string fullName);
|
||||||
Task DeleteItemAsync(string contentProviderId, string fullName);
|
Task DeleteItemAsync(string contentProviderId, string fullName);
|
||||||
Task MoveItemAsync(string contentProviderId, string fullName, string newPath);
|
Task MoveItemAsync(string contentProviderId, string fullName, string newPath);
|
||||||
|
|
||||||
|
//TODO: CancellationToken https://github.com/nenoNaninu/TypedSignalR.Client/issues/120
|
||||||
Task FlushWriterAsync(string transactionId);
|
Task FlushWriterAsync(string transactionId);
|
||||||
Task InitializeRemoteWriter(string contentProviderId, string transactionId, string nativePath);
|
Task InitializeRemoteWriter(string contentProviderId, string transactionId, string nativePath);
|
||||||
|
|
||||||
|
//TODO: CancellationToken https://github.com/nenoNaninu/TypedSignalR.Client/issues/120
|
||||||
Task WriteBytesAsync(string transactionId, string data, int index);
|
Task WriteBytesAsync(string transactionId, string data, int index);
|
||||||
Task CloseWriterAsync(string transactionId);
|
Task CloseWriterAsync(string transactionId);
|
||||||
}
|
}
|
||||||
@@ -68,10 +68,10 @@ public class SignalRConnection : IRemoteConnection, IAsyncInitable<string>
|
|||||||
public async Task MoveItemAsync(string contentProviderId, FullName fullName, FullName newPath)
|
public async Task MoveItemAsync(string contentProviderId, FullName fullName, FullName newPath)
|
||||||
=> await _client.MoveItemAsync(contentProviderId, fullName.Path, newPath.Path);
|
=> await _client.MoveItemAsync(contentProviderId, fullName.Path, newPath.Path);
|
||||||
|
|
||||||
public async Task WriteBytesAsync(string transactionId, byte[] data, int? index)
|
public async Task WriteBytesAsync(string transactionId, byte[] data, int? index, CancellationToken cancellationToken = default)
|
||||||
=> await _client.WriteBytesAsync(transactionId, Convert.ToBase64String(data), index ?? -1);
|
=> await _client.WriteBytesAsync(transactionId, Convert.ToBase64String(data), index ?? -1);
|
||||||
|
|
||||||
public async Task FlushWriterAsync(string transactionId)
|
public async Task FlushWriterAsync(string transactionId, CancellationToken cancellationToken = default)
|
||||||
=> await _client.FlushWriterAsync(transactionId);
|
=> await _client.FlushWriterAsync(transactionId);
|
||||||
|
|
||||||
public async Task InitializeRemoteWriter(string contentProviderId, string transactionId, NativePath nativePath)
|
public async Task InitializeRemoteWriter(string contentProviderId, string transactionId, NativePath nativePath)
|
||||||
|
|||||||
Reference in New Issue
Block a user