Using CancellationToken

This commit is contained in:
2022-02-04 00:17:03 +01:00
parent 3f8309dc0f
commit 2e832f72d2
23 changed files with 1509 additions and 147 deletions

View File

@@ -2,55 +2,68 @@
{
public class AsyncEventHandler<TSender, TArg>
{
private readonly List<Func<TSender, TArg, Task>> _handlers;
private readonly Action<Func<TSender, TArg, Task>> _add;
private readonly Action<Func<TSender, TArg, Task>> _remove;
private readonly object _guard = new();
private readonly List<Func<TSender, TArg, CancellationToken, Task>> _handlers;
private readonly Action<Func<TSender, TArg, CancellationToken, Task>> _add;
private readonly Action<Func<TSender, TArg, CancellationToken, Task>> _remove;
public IReadOnlyList<Func<TSender, TArg, Task>> Handlers { get; }
public IReadOnlyList<Func<TSender, TArg, CancellationToken, Task>> Handlers { get; }
public AsyncEventHandler(Action<Func<TSender, TArg, Task>>? add = null, Action<Func<TSender, TArg, Task>>? remove = null)
public AsyncEventHandler(Action<Func<TSender, TArg, CancellationToken, Task>>? add = null, Action<Func<TSender, TArg, CancellationToken, Task>>? remove = null)
{
_handlers = new List<Func<TSender, TArg, Task>>();
_handlers = new List<Func<TSender, TArg, CancellationToken, Task>>();
Handlers = _handlers.AsReadOnly();
_add = add ?? AddInternal;
_remove = remove ?? RemoveInternal;
}
public void Add(Func<TSender, TArg, Task> handler)
public void Add(Func<TSender, TArg, CancellationToken, Task> handler)
{
_add.Invoke(handler);
lock (_guard)
{
_add.Invoke(handler);
}
}
public void Remove(Func<TSender, TArg, Task> handler)
public void Remove(Func<TSender, TArg, CancellationToken, Task> handler)
{
_remove.Invoke(handler);
lock (_guard)
{
_remove.Invoke(handler);
}
}
private void AddInternal(Func<TSender, TArg, Task> handler)
private void AddInternal(Func<TSender, TArg, CancellationToken, Task> handler)
{
_handlers.Add(handler);
}
private void RemoveInternal(Func<TSender, TArg, Task> handler)
private void RemoveInternal(Func<TSender, TArg, CancellationToken, Task> handler)
{
_handlers.Remove(handler);
}
public async Task InvokeAsync(TSender sender, TArg args)
public async Task InvokeAsync(TSender sender, TArg args, CancellationToken token = default)
{
foreach(var handler in _handlers)
List<Func<TSender, TArg, CancellationToken, Task>>? handlers;
lock (_guard)
{
await handler(sender, args);
handlers = _handlers;
}
foreach (var handler in handlers)
{
await handler(sender, args, token);
}
}
public static AsyncEventHandler<TSender, TArg> operator +(AsyncEventHandler<TSender, TArg> obj, Func<TSender, TArg, Task> handler)
public static AsyncEventHandler<TSender, TArg> operator +(AsyncEventHandler<TSender, TArg> obj, Func<TSender, TArg, CancellationToken, Task> handler)
{
obj.Add(handler);
return obj;
}
public static AsyncEventHandler<TSender, TArg> operator -(AsyncEventHandler<TSender, TArg> obj, Func<TSender, TArg, Task> handler)
public static AsyncEventHandler<TSender, TArg> operator -(AsyncEventHandler<TSender, TArg> obj, Func<TSender, TArg, CancellationToken, Task> handler)
{
obj.Remove(handler);
return obj;
@@ -58,11 +71,11 @@
}
public class AsyncEventHandler<TArg> : AsyncEventHandler<object?, TArg>
{
public AsyncEventHandler(Action<Func<object?, TArg, Task>>? add = null, Action<Func<object?, TArg, Task>>? remove = null) : base(add, remove) { }
public AsyncEventHandler(Action<Func<object?, TArg, CancellationToken, Task>>? add = null, Action<Func<object?, TArg, CancellationToken, Task>>? remove = null) : base(add, remove) { }
}
public class AsyncEventHandler : AsyncEventHandler<AsyncEventArgs>
{
public AsyncEventHandler(Action<Func<object?, AsyncEventArgs, Task>>? add = null, Action<Func<object?, AsyncEventArgs, Task>>? remove = null) : base(add, remove) { }
public AsyncEventHandler(Action<Func<object?, AsyncEventArgs, CancellationToken, Task>>? add = null, Action<Func<object?, AsyncEventArgs, CancellationToken, Task>>? remove = null) : base(add, remove) { }
}
}

View File

@@ -11,6 +11,9 @@ namespace FileTime.Core.Components
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
private string? _lastPath;
private readonly object _guardSetCurrentSelectedItemCTS = new object();
private CancellationTokenSource? _setCurrentSelectedItemCTS;
public int CurrentSelectedIndex { get; private set; }
public AsyncEventHandler CurrentLocationChanged = new();
@@ -21,7 +24,7 @@ namespace FileTime.Core.Components
await SetCurrentLocation(currentPath);
}
public Task<IContainer> GetCurrentLocation()
public Task<IContainer> GetCurrentLocation(CancellationToken token = default)
{
return Task.FromResult(_currentLocation);
}
@@ -49,24 +52,36 @@ namespace FileTime.Core.Components
return Task.FromResult(_currentSelectedItem);
}
public async Task SetCurrentSelectedItem(IItem? value)
public async Task SetCurrentSelectedItem(IItem? value, bool secondary = false)
{
if (_currentSelectedItem != value)
{
IItem? itemToSelect = null;
if (value != null)
{
itemToSelect = (await _currentLocation.GetItems())?.FirstOrDefault(i =>
i.FullName == null && value?.FullName == null
itemToSelect = (await _currentLocation.GetItems())?.FirstOrDefault(i =>
i.FullName == null && value?.FullName == null
? i.Name == value?.Name
: i.FullName == value?.FullName);
if (itemToSelect == null) throw new IndexOutOfRangeException("Provided item does not exists in the current container.");
}
CancellationToken newToken;
lock (_guardSetCurrentSelectedItemCTS)
{
_setCurrentSelectedItemCTS?.Cancel();
_setCurrentSelectedItemCTS = new CancellationTokenSource();
newToken = _setCurrentSelectedItemCTS.Token;
}
_currentSelectedItem = itemToSelect;
_lastPath = GetCommonPath(_lastPath, itemToSelect?.FullName);
CurrentSelectedIndex = await GetItemIndex(itemToSelect);
await CurrentSelectedItemChanged.InvokeAsync(this, AsyncEventArgs.Empty);
var newCurrentSelectedIndex = await GetItemIndex(itemToSelect, newToken);
if (newToken.IsCancellationRequested) return;
CurrentSelectedIndex = newCurrentSelectedIndex;
await CurrentSelectedItemChanged.InvokeAsync(this, AsyncEventArgs.Empty, newToken);
}
}
public async Task<IItem?> GetItemByLastPath(IContainer? container = null)
@@ -85,21 +100,16 @@ namespace FileTime.Core.Components
var itemNameToSelect = _lastPath
.Split(Constants.SeparatorChar)
.Skip(
containerFullName == null
? 0
: containerFullName
.Split(Constants.SeparatorChar)
.Count())
.Skip((containerFullName?.Split(Constants.SeparatorChar).Length) ?? 0)
.FirstOrDefault();
return (await container.GetItems())?.FirstOrDefault(i => i.Name == itemNameToSelect);
}
private string GetCommonPath(string? oldPath, string? newPath)
private static string GetCommonPath(string? oldPath, string? newPath)
{
var oldPathParts = oldPath?.Split(Constants.SeparatorChar) ?? new string[0];
var newPathParts = newPath?.Split(Constants.SeparatorChar) ?? new string[0];
var oldPathParts = oldPath?.Split(Constants.SeparatorChar) ?? Array.Empty<string>();
var newPathParts = newPath?.Split(Constants.SeparatorChar) ?? Array.Empty<string>();
var commonPathParts = new List<string>();
@@ -126,13 +136,13 @@ namespace FileTime.Core.Components
return string.Join(Constants.SeparatorChar, commonPathParts);
}
private async Task HandleCurrentLocationRefresh(object? sender, AsyncEventArgs e)
private async Task HandleCurrentLocationRefresh(object? sender, AsyncEventArgs e, CancellationToken token = default)
{
var currentSelectedName = (await GetCurrentSelectedItem())?.FullName ?? (await GetItemByLastPath())?.FullName;
var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
var currentLocationItems = (await (await GetCurrentLocation(token)).GetItems(token))!;
if (currentSelectedName != null)
{
await SetCurrentSelectedItem(currentLocationItems.FirstOrDefault(i => i.FullName == currentSelectedName) ?? currentLocationItems.FirstOrDefault());
await SetCurrentSelectedItem(currentLocationItems.FirstOrDefault(i => i.FullName == currentSelectedName) ?? currentLocationItems[0]);
}
else if (currentLocationItems.Count > 0)
{
@@ -184,7 +194,7 @@ namespace FileTime.Core.Components
if (await GetCurrentSelectedItem() != null)
{
(await GetCurrentLocation())?.Refresh();
(await GetCurrentLocation())?.RefreshAsync();
IItem? newSelectedItem = null;
foreach (var item in currentPossibleItems)
@@ -197,7 +207,7 @@ namespace FileTime.Core.Components
}
}
if(newSelectedItem != null)
if (newSelectedItem != null)
{
newSelectedItem = (await (await GetCurrentLocation()).GetItems())?.FirstOrDefault(i => i.Name == newSelectedItem.Name);
}
@@ -272,11 +282,12 @@ namespace FileTime.Core.Components
public async Task OpenContainer(IContainer container) => await SetCurrentLocation(container);
private async Task<int> GetItemIndex(IItem? item)
private async Task<int> GetItemIndex(IItem? item, CancellationToken token)
{
if (item == null) return -1;
var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
var currentLocationItems = (await (await GetCurrentLocation(token)).GetItems(token))!;
if (token.IsCancellationRequested) return -1;
for (var i = 0; i < currentLocationItems.Count; i++)
{
if (currentLocationItems[i] == item) return i;

View File

@@ -9,7 +9,7 @@ namespace FileTime.Core.Models
Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default);
Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default);
Task Refresh();
Task RefreshAsync(CancellationToken token = default);
Task<IItem?> GetByPath(string path, bool acceptDeepestMatch = false);
Task<IContainer> CreateContainer(string name);
Task<IElement> CreateElement(string name);

View File

@@ -35,11 +35,11 @@ namespace FileTime.Core.Models
public AsyncEventHandler Refreshed { get; }
private void RefreshAddBase(Func<object?, AsyncEventArgs, Task> handler)
private void RefreshAddBase(Func<object?, AsyncEventArgs, CancellationToken, Task> handler)
{
BaseContainer.Refreshed.Add(handler);
}
private void RefreshRemoveBase(Func<object?, AsyncEventArgs, Task> handler)
private void RefreshRemoveBase(Func<object?, AsyncEventArgs, CancellationToken, Task> handler)
{
BaseContainer.Refreshed.Add(handler);
}
@@ -52,7 +52,7 @@ namespace FileTime.Core.Models
bool isTransitive = false,
string? virtualContainerName = null)
{
Refreshed = new (RefreshAddBase, RefreshRemoveBase);
Refreshed = new(RefreshAddBase, RefreshRemoveBase);
BaseContainer = baseContainer;
_containerTransformators = containerTransformators;
_elementTransformators = elementTransformators;
@@ -67,10 +67,10 @@ namespace FileTime.Core.Models
await InitItems();
}
private async Task InitItems()
private async Task InitItems(CancellationToken token = default)
{
Containers = _containerTransformators.Aggregate((await BaseContainer.GetContainers())?.AsEnumerable(), (a, t) => t(a!))?.ToList()?.AsReadOnly();
Elements = _elementTransformators.Aggregate((await BaseContainer.GetElements())?.AsEnumerable(), (a, t) => t(a!))?.ToList()?.AsReadOnly();
Containers = _containerTransformators.Aggregate((await BaseContainer.GetContainers(token))?.AsEnumerable(), (a, t) => t(a!))?.ToList()?.AsReadOnly();
Elements = _elementTransformators.Aggregate((await BaseContainer.GetElements(token))?.AsEnumerable(), (a, t) => t(a!))?.ToList()?.AsReadOnly();
Items = (Elements != null
? Containers?.Cast<IItem>().Concat(Elements)
@@ -82,10 +82,10 @@ namespace FileTime.Core.Models
public IContainer? GetParent() => BaseContainer.GetParent();
public async Task Refresh()
public async Task RefreshAsync(CancellationToken token = default)
{
await BaseContainer.Refresh();
await InitItems();
await BaseContainer.RefreshAsync(token);
await InitItems(token);
}
public IContainer GetRealContainer() =>

View File

@@ -57,7 +57,7 @@ namespace FileTime.Core.Providers
public Task<bool> IsExists(string name) => throw new NotImplementedException();
public async Task Refresh() => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty);
public async Task RefreshAsync(CancellationToken token = default) => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty);
public Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default) => Task.FromResult(_items);
public Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default) => Task.FromResult(_containers);

View File

@@ -103,7 +103,7 @@ namespace FileTime.Core.Timeline
public async Task<bool> IsExists(string name) => (await GetItems())?.Any(i => i.Name == name) ?? false;
public async Task Refresh() => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty);
public async Task RefreshAsync(CancellationToken token = default) => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
public Task Rename(string newName) => Task.CompletedTask;

View File

@@ -84,7 +84,7 @@ namespace FileTime.Core.Timeline
throw new NotImplementedException();
}
public Task Refresh() => Task.CompletedTask;
public Task RefreshAsync(CancellationToken token = default) => Task.CompletedTask;
public Task Rename(string newName) => throw new NotSupportedException();