Cancel commands

This commit is contained in:
2023-07-28 13:08:46 +02:00
parent 12c9aa039a
commit ee42e38e45
19 changed files with 75 additions and 18 deletions

View File

@@ -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();
} }

View File

@@ -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();
} }

View File

@@ -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();
} }

View File

@@ -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);
} }

View File

@@ -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);

View File

@@ -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)

View File

@@ -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();
} }

View File

@@ -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()
{
}
} }

View File

@@ -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()
{
}
} }

View File

@@ -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

View File

@@ -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();

View File

@@ -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)
{ {

View File

@@ -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

View File

@@ -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"

View File

@@ -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;

View File

@@ -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)

View File

@@ -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);
} }

View File

@@ -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);
} }

View File

@@ -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)