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

@@ -43,7 +43,7 @@ namespace FileTime.ConsoleUI.App.UI
if (paneState == null) throw new Exception($"{nameof(paneState)} can not be null"); if (paneState == null) throw new Exception($"{nameof(paneState)} can not be null");
Tab = pane; Tab = pane;
Tab.CurrentLocationChanged.Add((_, _) => { _currentDisplayStartY = 0; return Task.CompletedTask; }); Tab.CurrentLocationChanged.Add((_, _, _) => { _currentDisplayStartY = 0; return Task.CompletedTask; });
TabState = paneState; TabState = paneState;
} }
@@ -70,7 +70,7 @@ namespace FileTime.ConsoleUI.App.UI
if (currentLocation.GetParent() is var parentContainer && parentContainer is not null) if (currentLocation.GetParent() is var parentContainer && parentContainer is not null)
{ {
await parentContainer.Refresh(); await parentContainer.RefreshAsync();
await PrintColumn( await PrintColumn(
currentVirtualContainer != null currentVirtualContainer != null
@@ -96,7 +96,7 @@ namespace FileTime.ConsoleUI.App.UI
if (token.IsCancellationRequested) return; if (token.IsCancellationRequested) return;
await currentLocation.Refresh(); await currentLocation.RefreshAsync();
await CheckAndSetCurrentDisplayStartY(); await CheckAndSetCurrentDisplayStartY();
await PrintColumn( await PrintColumn(
@@ -112,7 +112,7 @@ namespace FileTime.ConsoleUI.App.UI
if (currentSelectedItem is IContainer selectedContainer) if (currentSelectedItem is IContainer selectedContainer)
{ {
await selectedContainer.Refresh(); await selectedContainer.RefreshAsync();
selectedContainer = currentVirtualContainer != null selectedContainer = currentVirtualContainer != null
? currentVirtualContainer.CloneVirtualChainFor(selectedContainer, v => v.IsTransitive) ? currentVirtualContainer.CloneVirtualChainFor(selectedContainer, v => v.IsTransitive)

View File

@@ -2,55 +2,68 @@
{ {
public class AsyncEventHandler<TSender, TArg> public class AsyncEventHandler<TSender, TArg>
{ {
private readonly List<Func<TSender, TArg, Task>> _handlers; private readonly object _guard = new();
private readonly Action<Func<TSender, TArg, Task>> _add; private readonly List<Func<TSender, TArg, CancellationToken, Task>> _handlers;
private readonly Action<Func<TSender, TArg, Task>> _remove; 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(); Handlers = _handlers.AsReadOnly();
_add = add ?? AddInternal; _add = add ?? AddInternal;
_remove = remove ?? RemoveInternal; _remove = remove ?? RemoveInternal;
} }
public void Add(Func<TSender, TArg, Task> handler) public void Add(Func<TSender, TArg, CancellationToken, Task> handler)
{
lock (_guard)
{ {
_add.Invoke(handler); _add.Invoke(handler);
} }
}
public void Remove(Func<TSender, TArg, Task> handler) public void Remove(Func<TSender, TArg, CancellationToken, Task> handler)
{
lock (_guard)
{ {
_remove.Invoke(handler); _remove.Invoke(handler);
} }
}
private void AddInternal(Func<TSender, TArg, Task> handler) private void AddInternal(Func<TSender, TArg, CancellationToken, Task> handler)
{ {
_handlers.Add(handler); _handlers.Add(handler);
} }
private void RemoveInternal(Func<TSender, TArg, Task> handler) private void RemoveInternal(Func<TSender, TArg, CancellationToken, Task> handler)
{ {
_handlers.Remove(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); obj.Add(handler);
return obj; 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); obj.Remove(handler);
return obj; return obj;
@@ -58,11 +71,11 @@
} }
public class AsyncEventHandler<TArg> : AsyncEventHandler<object?, TArg> 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 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. #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
private string? _lastPath; private string? _lastPath;
private readonly object _guardSetCurrentSelectedItemCTS = new object();
private CancellationTokenSource? _setCurrentSelectedItemCTS;
public int CurrentSelectedIndex { get; private set; } public int CurrentSelectedIndex { get; private set; }
public AsyncEventHandler CurrentLocationChanged = new(); public AsyncEventHandler CurrentLocationChanged = new();
@@ -21,7 +24,7 @@ namespace FileTime.Core.Components
await SetCurrentLocation(currentPath); await SetCurrentLocation(currentPath);
} }
public Task<IContainer> GetCurrentLocation() public Task<IContainer> GetCurrentLocation(CancellationToken token = default)
{ {
return Task.FromResult(_currentLocation); return Task.FromResult(_currentLocation);
} }
@@ -49,7 +52,7 @@ namespace FileTime.Core.Components
return Task.FromResult(_currentSelectedItem); return Task.FromResult(_currentSelectedItem);
} }
public async Task SetCurrentSelectedItem(IItem? value) public async Task SetCurrentSelectedItem(IItem? value, bool secondary = false)
{ {
if (_currentSelectedItem != value) if (_currentSelectedItem != value)
{ {
@@ -63,10 +66,22 @@ namespace FileTime.Core.Components
if (itemToSelect == null) throw new IndexOutOfRangeException("Provided item does not exists in the current container."); 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; _currentSelectedItem = itemToSelect;
_lastPath = GetCommonPath(_lastPath, itemToSelect?.FullName); _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) public async Task<IItem?> GetItemByLastPath(IContainer? container = null)
@@ -85,21 +100,16 @@ namespace FileTime.Core.Components
var itemNameToSelect = _lastPath var itemNameToSelect = _lastPath
.Split(Constants.SeparatorChar) .Split(Constants.SeparatorChar)
.Skip( .Skip((containerFullName?.Split(Constants.SeparatorChar).Length) ?? 0)
containerFullName == null
? 0
: containerFullName
.Split(Constants.SeparatorChar)
.Count())
.FirstOrDefault(); .FirstOrDefault();
return (await container.GetItems())?.FirstOrDefault(i => i.Name == itemNameToSelect); 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 oldPathParts = oldPath?.Split(Constants.SeparatorChar) ?? Array.Empty<string>();
var newPathParts = newPath?.Split(Constants.SeparatorChar) ?? new string[0]; var newPathParts = newPath?.Split(Constants.SeparatorChar) ?? Array.Empty<string>();
var commonPathParts = new List<string>(); var commonPathParts = new List<string>();
@@ -126,13 +136,13 @@ namespace FileTime.Core.Components
return string.Join(Constants.SeparatorChar, commonPathParts); 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 currentSelectedName = (await GetCurrentSelectedItem())?.FullName ?? (await GetItemByLastPath())?.FullName;
var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!; var currentLocationItems = (await (await GetCurrentLocation(token)).GetItems(token))!;
if (currentSelectedName != null) 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) else if (currentLocationItems.Count > 0)
{ {
@@ -184,7 +194,7 @@ namespace FileTime.Core.Components
if (await GetCurrentSelectedItem() != null) if (await GetCurrentSelectedItem() != null)
{ {
(await GetCurrentLocation())?.Refresh(); (await GetCurrentLocation())?.RefreshAsync();
IItem? newSelectedItem = null; IItem? newSelectedItem = null;
foreach (var item in currentPossibleItems) 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); 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); 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; 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++) for (var i = 0; i < currentLocationItems.Count; i++)
{ {
if (currentLocationItems[i] == item) return 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<IContainer>?> GetContainers(CancellationToken token = default);
Task<IReadOnlyList<IElement>?> GetElements(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<IItem?> GetByPath(string path, bool acceptDeepestMatch = false);
Task<IContainer> CreateContainer(string name); Task<IContainer> CreateContainer(string name);
Task<IElement> CreateElement(string name); Task<IElement> CreateElement(string name);

View File

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

View File

@@ -57,7 +57,7 @@ namespace FileTime.Core.Providers
public Task<bool> IsExists(string name) => throw new NotImplementedException(); 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<IItem>?> GetItems(CancellationToken token = default) => Task.FromResult(_items);
public Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default) => Task.FromResult(_containers); 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<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; public Task Rename(string newName) => Task.CompletedTask;

View File

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

View File

@@ -6,6 +6,7 @@ using System.Linq;
using FileTime.App.Core.Tab; using FileTime.App.Core.Tab;
using System.Threading.Tasks; using System.Threading.Tasks;
using FileTime.Core.Models; using FileTime.Core.Models;
using System.Threading;
namespace FileTime.Avalonia.Application namespace FileTime.Avalonia.Application
{ {
@@ -71,12 +72,13 @@ namespace FileTime.Avalonia.Application
} }
} }
private async Task TabItemMarked(TabState tabState, AbsolutePath item) private async Task TabItemMarked(TabState tabState, AbsolutePath item, CancellationToken token = default)
{ {
var tabContainer = Tabs.FirstOrDefault(t => t.TabState == tabState); var tabContainer = Tabs.FirstOrDefault(t => t.TabState == tabState);
if (tabContainer != null) if (tabContainer != null)
{ {
var item2 = (await tabContainer.CurrentLocation.GetItems()).FirstOrDefault(i => i.Item.FullName == item.Path); var item2 = (await tabContainer.CurrentLocation.GetItems(token)).FirstOrDefault(i => i.Item.FullName == item.Path);
if (token.IsCancellationRequested) return;
if (item2 != null) if (item2 != null)
{ {
item2.IsMarked = true; item2.IsMarked = true;
@@ -84,12 +86,13 @@ namespace FileTime.Avalonia.Application
} }
} }
private async Task TabItemUnmarked(TabState tabState, AbsolutePath item) private async Task TabItemUnmarked(TabState tabState, AbsolutePath item, CancellationToken token = default)
{ {
var tabContainer = Tabs.FirstOrDefault(t => t.TabState == tabState); var tabContainer = Tabs.FirstOrDefault(t => t.TabState == tabState);
if (tabContainer != null) if (tabContainer != null)
{ {
var item2 = (await tabContainer.CurrentLocation.GetItems()).FirstOrDefault(i => i.Item.FullName == item.Path); var item2 = (await tabContainer.CurrentLocation.GetItems(token)).FirstOrDefault(i => i.Item.FullName == item.Path);
if (token.IsCancellationRequested) return;
if (item2 != null) if (item2 != null)
{ {
item2.IsMarked = false; item2.IsMarked = false;

View File

@@ -1,3 +1,4 @@
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using FileTime.Avalonia.ViewModels; using FileTime.Avalonia.ViewModels;
@@ -5,6 +6,6 @@ namespace FileTime.Avalonia.Application
{ {
public interface INewItemProcessor public interface INewItemProcessor
{ {
Task UpdateMarkedItems(ContainerViewModel containerViewModel); Task UpdateMarkedItems(ContainerViewModel containerViewModel, CancellationToken token = default);
} }
} }

View File

@@ -10,7 +10,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using FileTime.App.Core.Tab; using FileTime.App.Core.Tab;
using System.Collections.Generic; using System.Threading;
namespace FileTime.Avalonia.Application namespace FileTime.Avalonia.Application
{ {
@@ -40,18 +40,27 @@ namespace FileTime.Avalonia.Application
private IItemViewModel? _selectedItem; private IItemViewModel? _selectedItem;
[Obsolete($"Use {nameof(SetSelectedItemAsync)} instead.")]
public IItemViewModel? SelectedItem public IItemViewModel? SelectedItem
{ {
get => _selectedItem; get => _selectedItem;
set set
{ {
if (_selectedItem != value)// && value != null if (value != null)
{
SetSelectedItemAsync(value, true).Wait();
}
}
}
public async Task SetSelectedItemAsync(IItemViewModel? value, bool fromDataBinding = false)
{
if (_selectedItem != value)
{ {
_selectedItem = value; _selectedItem = value;
OnPropertyChanged("SelectedItem"); OnPropertyChanged(nameof(SelectedItem));
SelectedItemChanged().Wait(); await Tab.SetCurrentSelectedItem(SelectedItem?.Item, fromDataBinding);
}
} }
} }
@@ -96,18 +105,20 @@ namespace FileTime.Avalonia.Application
return parent; return parent;
} }
private async Task Tab_CurrentLocationChanged(object? sender, AsyncEventArgs e) private async Task Tab_CurrentLocationChanged(object? sender, AsyncEventArgs e, CancellationToken token = default)
{ {
var currentLocation = await Tab.GetCurrentLocation(); var currentLocation = await Tab.GetCurrentLocation(token);
var parent = GenerateParent(currentLocation); var parent = GenerateParent(currentLocation);
CurrentLocation = new ContainerViewModel(this, parent, currentLocation, ItemNameConverterService); CurrentLocation = new ContainerViewModel(this, parent, currentLocation, ItemNameConverterService);
await CurrentLocation.Init(); await CurrentLocation.Init(token: token);
if (token.IsCancellationRequested) return;
if (parent != null) if (parent != null)
{ {
parent.ChildrenToAdopt.Add(CurrentLocation); parent.ChildrenToAdopt.Add(CurrentLocation);
Parent = parent; Parent = parent;
await Parent.Init(); await Parent.Init(token: token);
} }
else else
{ {
@@ -115,42 +126,47 @@ namespace FileTime.Avalonia.Application
} }
} }
private async Task Tab_CurrentSelectedItemChanged(object? sender, AsyncEventArgs e) private async Task Tab_CurrentSelectedItemChanged(object? sender, AsyncEventArgs e, CancellationToken token = default)
{ {
await UpdateCurrentSelectedItem(); await UpdateCurrentSelectedItem(token);
} }
public async Task UpdateCurrentSelectedItem() public async Task UpdateCurrentSelectedItem(CancellationToken token = default)
{ {
try try
{ {
var tabCurrentSelectenItem = await Tab.GetCurrentSelectedItem(); var tabCurrentSelectenItem = await Tab.GetCurrentSelectedItem();
if (token.IsCancellationRequested) return;
IItemViewModel? currentSelectenItem = null; IItemViewModel? currentSelectenItem = null;
if (tabCurrentSelectenItem == null) if (tabCurrentSelectenItem == null)
{ {
SelectedItem = null; await SetSelectedItemAsync(null);
ChildContainer = null; ChildContainer = null;
} }
else else
{ {
currentSelectenItem = (await _currentLocation.GetItems()).FirstOrDefault(i => i.Item.Name == tabCurrentSelectenItem.Name); currentSelectenItem = (await _currentLocation.GetItems(token)).FirstOrDefault(i => i.Item.Name == tabCurrentSelectenItem.Name);
if (currentSelectenItem is ContainerViewModel currentSelectedContainer) if (currentSelectenItem is ContainerViewModel currentSelectedContainer)
{ {
SelectedItem = currentSelectedContainer; await SetSelectedItemAsync(currentSelectedContainer);
ChildContainer = currentSelectedContainer; ChildContainer = currentSelectedContainer;
} }
else if (currentSelectenItem is ElementViewModel element) else if (currentSelectenItem is ElementViewModel element)
{ {
SelectedItem = element; await SetSelectedItemAsync(element);
ChildContainer = null; ChildContainer = null;
} }
else else
{ {
SelectedItem = null; await SetSelectedItemAsync(null);
ChildContainer = null; ChildContainer = null;
} }
} }
if (token.IsCancellationRequested) return;
var items = await _currentLocation.GetItems(); var items = await _currentLocation.GetItems();
if (items?.Count > 0) if (items?.Count > 0)
{ {
@@ -173,6 +189,7 @@ namespace FileTime.Avalonia.Application
var child = item; var child = item;
while (child is ContainerViewModel containerViewModel && containerViewModel.Container.IsLoaded) while (child is ContainerViewModel containerViewModel && containerViewModel.Container.IsLoaded)
{ {
if (token.IsCancellationRequested) return;
var activeChildItem = await Tab.GetItemByLastPath(containerViewModel.Container); var activeChildItem = await Tab.GetItemByLastPath(containerViewModel.Container);
child = (await containerViewModel.GetItems()).FirstOrDefault(i => i.Item == activeChildItem); child = (await containerViewModel.GetItems()).FirstOrDefault(i => i.Item == activeChildItem);
if (child != null) if (child != null)
@@ -206,15 +223,6 @@ namespace FileTime.Avalonia.Application
} }
} }
private async Task SelectedItemChanged()
{
try
{
await Tab.SetCurrentSelectedItem(SelectedItem?.Item);
}
catch { }
}
public async Task SetCurrentSelectedItem(IItem newItem) public async Task SetCurrentSelectedItem(IItem newItem)
{ {
try try
@@ -309,13 +317,14 @@ namespace FileTime.Avalonia.Application
await _tabState.MakrCurrentItem(); await _tabState.MakrCurrentItem();
} }
public async Task UpdateMarkedItems(ContainerViewModel containerViewModel) public async Task UpdateMarkedItems(ContainerViewModel containerViewModel, CancellationToken token = default)
{ {
if (containerViewModel == CurrentLocation && containerViewModel.Container.IsLoaded) if (containerViewModel == CurrentLocation && containerViewModel.Container.IsLoaded)
{ {
if (token.IsCancellationRequested) return;
var selectedItems = TabState.GetCurrentMarkedItems(containerViewModel.Container); var selectedItems = TabState.GetCurrentMarkedItems(containerViewModel.Container);
foreach (var item in await containerViewModel.GetItems()) foreach (var item in await containerViewModel.GetItems(token))
{ {
item.IsMarked = selectedItems.Any(c => c.Path == item.Item.FullName); item.IsMarked = selectedItems.Any(c => c.Path == item.Item.FullName);
} }

View File

@@ -10,6 +10,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using FileTime.Avalonia.Application; using FileTime.Avalonia.Application;
using System.Threading;
namespace FileTime.Avalonia.ViewModels namespace FileTime.Avalonia.ViewModels
{ {
@@ -130,21 +131,22 @@ namespace FileTime.Avalonia.ViewModels
public void InvalidateDisplayName() => OnPropertyChanged(nameof(DisplayName)); public void InvalidateDisplayName() => OnPropertyChanged(nameof(DisplayName));
public async Task Init(bool initializeChildren = true) public async Task Init(bool initializeChildren = true, CancellationToken token = default)
{ {
await Refresh(initializeChildren); await Refresh(initializeChildren, token);
} }
private async Task Container_Refreshed(object? sender, AsyncEventArgs e) private async Task Container_Refreshed(object? sender, AsyncEventArgs e, CancellationToken token = default)
{ {
await Refresh(false); await Refresh(false, token);
} }
[Obsolete($"Use the parametrizable version of {nameof(Refresh)}.")]
private async Task Refresh() private async Task Refresh()
{ {
await Refresh(true); await Refresh(true);
} }
private async Task Refresh(bool initializeChildren) private async Task Refresh(bool initializeChildren, CancellationToken token = default)
{ {
if (_isRefreshing) return; if (_isRefreshing) return;
@@ -157,6 +159,9 @@ namespace FileTime.Avalonia.ViewModels
var containers = (await _container.GetContainers())!.Select(c => AdoptOrReuseOrCreateItem(c, (c2) => new ContainerViewModel(_newItemProcessor, this, c2, ItemNameConverterService))).ToList(); var containers = (await _container.GetContainers())!.Select(c => AdoptOrReuseOrCreateItem(c, (c2) => new ContainerViewModel(_newItemProcessor, this, c2, ItemNameConverterService))).ToList();
var elements = (await _container.GetElements())!.Select(e => AdoptOrReuseOrCreateItem(e, (e2) => new ElementViewModel(e2, this, ItemNameConverterService))).ToList(); var elements = (await _container.GetElements())!.Select(e => AdoptOrReuseOrCreateItem(e, (e2) => new ElementViewModel(e2, this, ItemNameConverterService))).ToList();
if (token.IsCancellationRequested) return;
Exceptions = new List<Exception>(_container.Exceptions); Exceptions = new List<Exception>(_container.Exceptions);
foreach (var containerToRemove in _containers.Except(containers)) foreach (var containerToRemove in _containers.Except(containers))
@@ -168,7 +173,8 @@ namespace FileTime.Avalonia.ViewModels
{ {
foreach (var container in containers) foreach (var container in containers)
{ {
await container.Init(false); if (token.IsCancellationRequested) return;
await container.Init(false, token);
} }
} }
@@ -220,21 +226,21 @@ namespace FileTime.Avalonia.ViewModels
_items = new ObservableCollection<IItemViewModel>(); _items = new ObservableCollection<IItemViewModel>();
} }
public async Task<ObservableCollection<ContainerViewModel>> GetContainers() public async Task<ObservableCollection<ContainerViewModel>> GetContainers(CancellationToken token = default)
{ {
if (!_isInitialized) await Task.Run(Refresh); if (!_isInitialized) await Task.Run(async () => await Refresh(false, token), token);
return _containers; return _containers;
} }
public async Task<ObservableCollection<ElementViewModel>> GetElements() public async Task<ObservableCollection<ElementViewModel>> GetElements(CancellationToken token = default)
{ {
if (!_isInitialized) await Task.Run(Refresh); if (!_isInitialized) await Task.Run(async () => await Refresh(false, token), token);
return _elements; return _elements;
} }
public async Task<ObservableCollection<IItemViewModel>> GetItems() public async Task<ObservableCollection<IItemViewModel>> GetItems(CancellationToken token = default)
{ {
if (!_isInitialized) await Task.Run(Refresh); if (!_isInitialized) await Task.Run(async () => await Refresh(false, token), token);
return _items; return _items;
} }

View File

@@ -605,7 +605,7 @@ namespace FileTime.Avalonia.ViewModels
private async Task RefreshCurrentLocation() private async Task RefreshCurrentLocation()
{ {
await AppState.SelectedTab.CurrentLocation.Container.Refresh(); await AppState.SelectedTab.CurrentLocation.Container.RefreshAsync();
await AppState.SelectedTab.UpdateCurrentSelectedItem(); await AppState.SelectedTab.UpdateCurrentSelectedItem();
} }
@@ -640,7 +640,7 @@ namespace FileTime.Avalonia.ViewModels
var possibleContainer = await contentProvider.GetByPath(path); var possibleContainer = await contentProvider.GetByPath(path);
if (possibleContainer is IContainer container) if (possibleContainer is IContainer container)
{ {
AppState.SelectedTab.OpenContainer(container).Wait(); await AppState.SelectedTab.OpenContainer(container);
} }
//TODO: multiple possible content provider handler //TODO: multiple possible content provider handler
return; return;
@@ -873,7 +873,7 @@ namespace FileTime.Avalonia.ViewModels
var selectedItemName = AppState.SelectedTab.SelectedItem?.Item.Name; var selectedItemName = AppState.SelectedTab.SelectedItem?.Item.Name;
var currentLocationItems = await AppState.SelectedTab.CurrentLocation.GetItems(); var currentLocationItems = await AppState.SelectedTab.CurrentLocation.GetItems();
if (currentLocationItems.FirstOrDefault(i => i.Item.Name.ToLower() == AppState.RapidTravelText.ToLower()) is IItemViewModel matchItem) if (currentLocationItems.FirstOrDefault(i => string.Equals(i.Item.Name, AppState.RapidTravelText, StringComparison.OrdinalIgnoreCase)) is IItemViewModel matchItem)
{ {
await AppState.SelectedTab.SetCurrentSelectedItem(matchItem.Item); await AppState.SelectedTab.SetCurrentSelectedItem(matchItem.Item);
} }
@@ -882,7 +882,6 @@ namespace FileTime.Avalonia.ViewModels
await AppState.SelectedTab.MoveCursorToFirst(); await AppState.SelectedTab.MoveCursorToFirst();
} }
} }
} }
return true; return true;
@@ -899,7 +898,7 @@ namespace FileTime.Avalonia.ViewModels
} }
private void ReadInputs(List<Core.Interactions.InputElement> inputs, Func<Task> inputHandler) private void ReadInputs(List<Core.Interactions.InputElement> inputs, Func<Task> inputHandler)
{ {
Inputs = inputs.Select(i => new InputElementWrapper(i, i.DefaultValue)).ToList(); Inputs = inputs.ConvertAll(i => new InputElementWrapper(i, i.DefaultValue));
_inputHandler = inputHandler; _inputHandler = inputHandler;
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsyncEvent; using AsyncEvent;
using FileTime.Core.Command; using FileTime.Core.Command;
@@ -27,7 +28,7 @@ namespace FileTime.Avalonia.ViewModels
_commandTimeState.Command.ProgressChanged.Add(HandleProgressChange); _commandTimeState.Command.ProgressChanged.Add(HandleProgressChange);
} }
private Task HandleProgressChange(object? sender, AsyncEventArgs e) private Task HandleProgressChange(object? sender, AsyncEventArgs e, CancellationToken token = default)
{ {
Progress = _commandTimeState.Command.Progress; Progress = _commandTimeState.Command.Progress;
return Task.CompletedTask; return Task.CompletedTask;

View File

@@ -105,7 +105,7 @@ namespace FileTime.Avalonia.Views
&& sender is StyledElement control && sender is StyledElement control
&& control.DataContext is PlaceInfo placeInfo) && control.DataContext is PlaceInfo placeInfo)
{ {
ViewModel.OpenContainer(placeInfo.Container).Wait(); ViewModel.OpenContainer(placeInfo.Container);
e.Handled = true; e.Handled = true;
} }
} }

View File

@@ -69,7 +69,7 @@ namespace FileTime.Providers.Local
return remainingPath.Length == 0 ? rootContainer : await rootContainer.GetByPath(remainingPath, acceptDeepestMatch); return remainingPath.Length == 0 ? rootContainer : await rootContainer.GetByPath(remainingPath, acceptDeepestMatch);
} }
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<IContainer> Clone() => Task.FromResult((IContainer)this); public Task<IContainer> Clone() => Task.FromResult((IContainer)this);

View File

@@ -59,7 +59,7 @@ namespace FileTime.Providers.Local
if (_parent is LocalFolder parentFolder) if (_parent is LocalFolder parentFolder)
{ {
System.IO.File.Move(File.FullName, Path.Combine(parentFolder.Directory.FullName, newName)); System.IO.File.Move(File.FullName, Path.Combine(parentFolder.Directory.FullName, newName));
await _parent.Refresh(); await _parent.RefreshAsync();
} }
} }

View File

@@ -59,16 +59,22 @@ namespace FileTime.Providers.Local
public Task<IContainer> Clone() => Task.FromResult((IContainer)new LocalFolder(Directory, Provider, _parent)); public Task<IContainer> Clone() => Task.FromResult((IContainer)new LocalFolder(Directory, Provider, _parent));
public Task Refresh() public Task RefreshAsync(CancellationToken token = default)
{ {
_containers = new List<IContainer>(); _containers = new List<IContainer>();
_elements = new List<IElement>(); _elements = new List<IElement>();
_items = new List<IItem>();
_exceptions.Clear(); _exceptions.Clear();
try try
{ {
if (token.IsCancellationRequested) return Task.CompletedTask;
_containers = Directory.GetDirectories().Select(d => new LocalFolder(d, Provider, this)).OrderBy(d => d.Name).ToList().AsReadOnly(); _containers = Directory.GetDirectories().Select(d => new LocalFolder(d, Provider, this)).OrderBy(d => d.Name).ToList().AsReadOnly();
if (token.IsCancellationRequested) return Task.CompletedTask;
_elements = Directory.GetFiles().Select(f => new LocalFile(f, this, Provider)).OrderBy(f => f.Name).ToList().AsReadOnly(); _elements = Directory.GetFiles().Select(f => new LocalFile(f, this, Provider)).OrderBy(f => f.Name).ToList().AsReadOnly();
if (token.IsCancellationRequested) return Task.CompletedTask;
} }
catch (Exception e) catch (Exception e)
{ {
@@ -76,24 +82,24 @@ namespace FileTime.Providers.Local
} }
_items = _containers.Cast<IItem>().Concat(_elements).ToList().AsReadOnly(); _items = _containers.Cast<IItem>().Concat(_elements).ToList().AsReadOnly();
Refreshed?.InvokeAsync(this, AsyncEventArgs.Empty); Refreshed?.InvokeAsync(this, AsyncEventArgs.Empty, token);
return Task.CompletedTask; return Task.CompletedTask;
} }
public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default) public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
{ {
if (_items == null) await Refresh(); if (_items == null) await RefreshAsync(token);
return _items; return _items;
} }
public async Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default) public async Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
{ {
if (_containers == null) await Refresh(); if (_containers == null) await RefreshAsync(token);
return _containers; return _containers;
} }
public async Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default) public async Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default)
{ {
if (_elements == null) await Refresh(); if (_elements == null) await RefreshAsync(token);
return _elements; return _elements;
} }
@@ -118,7 +124,7 @@ namespace FileTime.Providers.Local
public async Task<IContainer> CreateContainer(string name) public async Task<IContainer> CreateContainer(string name)
{ {
Directory.CreateSubdirectory(name); Directory.CreateSubdirectory(name);
await Refresh(); await RefreshAsync();
return _containers!.FirstOrDefault(c => Provider.NormalizePath(c.Name) == Provider.NormalizePath(name))!; return _containers!.FirstOrDefault(c => Provider.NormalizePath(c.Name) == Provider.NormalizePath(name))!;
} }
@@ -126,7 +132,7 @@ namespace FileTime.Providers.Local
public async Task<IElement> CreateElement(string name) public async Task<IElement> CreateElement(string name)
{ {
using (File.Create(Path.Combine(Directory.FullName, name))) { } using (File.Create(Path.Combine(Directory.FullName, name))) { }
await Refresh(); await RefreshAsync();
return _elements!.FirstOrDefault(e => Provider.NormalizePath(e.Name) == Provider.NormalizePath(name))!; return _elements!.FirstOrDefault(e => Provider.NormalizePath(e.Name) == Provider.NormalizePath(name))!;
} }
@@ -150,7 +156,7 @@ namespace FileTime.Providers.Local
if (_parent is LocalFolder parentFolder) if (_parent is LocalFolder parentFolder)
{ {
System.IO.Directory.Move(Directory.FullName, Path.Combine(parentFolder.Directory.FullName, newName)); System.IO.Directory.Move(Directory.FullName, Path.Combine(parentFolder.Directory.FullName, newName));
await _parent.Refresh(); await _parent.RefreshAsync();
} }
} }

View File

@@ -51,7 +51,7 @@ namespace FileTime.Providers.Smb
_items = _rootContainers.OrderBy(c => c.Name).ToList().AsReadOnly(); _items = _rootContainers.OrderBy(c => c.Name).ToList().AsReadOnly();
} }
await Refresh(); await RefreshAsync();
return container; return container;
} }
@@ -89,7 +89,7 @@ namespace FileTime.Providers.Smb
public async Task<bool> IsExists(string name) => (await GetItems())?.Any(i => i.Name == name) ?? false; 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 bool CanHandlePath(string path) => path.StartsWith("smb://") || path.StartsWith(@"\\"); public bool CanHandlePath(string path) => path.StartsWith("smb://") || path.StartsWith(@"\\");

View File

@@ -88,7 +88,7 @@ namespace FileTime.Providers.Smb
throw new NotImplementedException(); throw new NotImplementedException();
} }
public async Task Refresh() public async Task RefreshAsync(CancellationToken token = default)
{ {
var containers = new List<IContainer>(); var containers = new List<IContainer>();
var elements = new List<IElement>(); var elements = new List<IElement>();
@@ -96,7 +96,7 @@ namespace FileTime.Providers.Smb
try try
{ {
var path = FullName![(_smbShare.FullName!.Length + 1)..]; var path = FullName![(_smbShare.FullName!.Length + 1)..];
(containers, elements) = await _smbShare.ListFolder(this, _smbShare.Name, path); (containers, elements) = await _smbShare.ListFolder(this, _smbShare.Name, path, token);
} }
catch { } catch { }
@@ -104,22 +104,22 @@ namespace FileTime.Providers.Smb
_elements = elements.AsReadOnly(); _elements = elements.AsReadOnly();
_items = _containers.Cast<IItem>().Concat(_elements).ToList().AsReadOnly(); _items = _containers.Cast<IItem>().Concat(_elements).ToList().AsReadOnly();
await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty); await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
} }
public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default) public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
{ {
if (_items == null) await Refresh(); if (_items == null) await RefreshAsync();
return _items; return _items;
} }
public async Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default) public async Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
{ {
if (_containers == null) await Refresh(); if (_containers == null) await RefreshAsync();
return _containers; return _containers;
} }
public async Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default) public async Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default)
{ {
if (_elements == null) await Refresh(); if (_elements == null) await RefreshAsync();
return _elements; return _elements;
} }
public Task<bool> CanOpen() => Task.FromResult(true); public Task<bool> CanOpen() => Task.FromResult(true);

View File

@@ -51,12 +51,12 @@ namespace FileTime.Providers.Smb
public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default) public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
{ {
if (_shares == null) await Refresh(); if (_shares == null) await RefreshAsync();
return _shares; return _shares;
} }
public async Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default) public async Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
{ {
if (_shares == null) await Refresh(); if (_shares == null) await RefreshAsync();
return _shares; return _shares;
} }
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default) public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default)
@@ -91,7 +91,7 @@ namespace FileTime.Providers.Smb
throw new NotImplementedException(); throw new NotImplementedException();
} }
public async Task Refresh() public async Task RefreshAsync(CancellationToken token = default)
{ {
List<string> shares = await _smbClientContext.RunWithSmbClientAsync((client) => client.ListShares(out var status)); List<string> shares = await _smbClientContext.RunWithSmbClientAsync((client) => client.ListShares(out var status));

View File

@@ -43,17 +43,17 @@ namespace FileTime.Providers.Smb
public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default) public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
{ {
if (_items == null) await Refresh(); if (_items == null) await RefreshAsync();
return _items; return _items;
} }
public async Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default) public async Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
{ {
if (_containers == null) await Refresh(); if (_containers == null) await RefreshAsync();
return _containers; return _containers;
} }
public async Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default) public async Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default)
{ {
if (_elements == null) await Refresh(); if (_elements == null) await RefreshAsync();
return _elements; return _elements;
} }
@@ -100,14 +100,14 @@ namespace FileTime.Providers.Smb
throw new NotImplementedException(); throw new NotImplementedException();
} }
public async Task Refresh() public async Task RefreshAsync(CancellationToken token = default)
{ {
var containers = new List<IContainer>(); var containers = new List<IContainer>();
var elements = new List<IElement>(); var elements = new List<IElement>();
try try
{ {
(containers, elements) = await ListFolder(this, Name, string.Empty); (containers, elements) = await ListFolder(this, Name, string.Empty, token);
} }
catch { } catch { }
@@ -115,10 +115,10 @@ namespace FileTime.Providers.Smb
_elements = elements.AsReadOnly(); _elements = elements.AsReadOnly();
_items = _containers.Cast<IItem>().Concat(_elements).ToList().AsReadOnly(); _items = _containers.Cast<IItem>().Concat(_elements).ToList().AsReadOnly();
await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty); await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
} }
public async Task<(List<IContainer> containers, List<IElement> elements)> ListFolder(IContainer parent, string shareName, string folderName) public async Task<(List<IContainer> containers, List<IElement> elements)> ListFolder(IContainer parent, string shareName, string folderName, CancellationToken token = default)
{ {
return await _smbClientContext.RunWithSmbClientAsync(client => return await _smbClientContext.RunWithSmbClientAsync(client =>
{ {