Using CancellationToken
This commit is contained in:
@@ -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) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() =>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user