Async core refactor

This commit is contained in:
2022-01-20 18:16:01 +01:00
parent 016100a565
commit 215503a4e3
33 changed files with 761 additions and 463 deletions

View File

@@ -65,9 +65,13 @@ namespace FileTime.App.Core.Tab
return false; return false;
} }
public IReadOnlyList<TabItem> GetCurrentSelectedItems() => public async Task<IReadOnlyList<TabItem>> GetCurrentSelectedItems()
SelectedItems.ContainsKey(Tab.CurrentLocation) {
? SelectedItems[Tab.CurrentLocation] var currentLocation = await Tab.GetCurrentLocation();
: new List<TabItem>().AsReadOnly();
return SelectedItems.ContainsKey(currentLocation)
? SelectedItems[currentLocation]
: new List<TabItem>().AsReadOnly();
}
} }
} }

View File

@@ -71,8 +71,12 @@ namespace FileTime.ConsoleUI.App.UI.Color
public void Clear() public void Clear()
{ {
Console.SetCursorPosition(0, 0); try
Write(new string(' ', Console.WindowHeight * Console.WindowWidth)); {
Console.SetCursorPosition(0, 0);
Write(new string(' ', Console.WindowHeight * Console.WindowWidth));
}
catch { }
} }
} }
} }

View File

@@ -8,12 +8,12 @@ namespace FileTime.ConsoleUI.App
{ {
private void CloseTab() private void CloseTab()
{ {
var currentTabIndex = _panes.IndexOf(_selectedTab!); var currentTabIndex = _tabs.IndexOf(_selectedTab!);
RemoveTab(_selectedTab!); RemoveTab(_selectedTab!);
if (_panes.Count > 0) if (_tabs.Count > 0)
{ {
_selectedTab = _panes[currentTabIndex == 0 ? 0 : currentTabIndex - 1]; _selectedTab = _tabs[currentTabIndex == 0 ? 0 : currentTabIndex - 1];
} }
else else
{ {
@@ -22,34 +22,36 @@ namespace FileTime.ConsoleUI.App
} }
} }
private void MoveCursorUp() => _selectedTab!.SelectPreviousItem(); private async Task MoveCursorUp() => await _selectedTab!.SelectPreviousItem();
private void MoveCursorDown() => _selectedTab!.SelectNextItem(); private async Task MoveCursorDown() => await _selectedTab!.SelectNextItem();
private void GoUp() => _selectedTab!.GoUp(); private async Task GoUp() => await _selectedTab!.GoUp();
private void Open() => _selectedTab!.Open(); private async Task Open() => await _selectedTab!.Open();
private void MoveCursorUpPage() => _selectedTab!.SelectPreviousItem(_renderers[_selectedTab].PageSize); private async Task MoveCursorUpPage() => await _selectedTab!.SelectPreviousItem(_renderers[_selectedTab].PageSize);
private void MoveCursorDownPage() => _selectedTab!.SelectNextItem(_renderers[_selectedTab].PageSize); private async Task MoveCursorDownPage() => await _selectedTab!.SelectNextItem(_renderers[_selectedTab].PageSize);
private void MoveCursorToTop() => _selectedTab!.SelectFirstItem(); private async Task MoveCursorToTop() => await _selectedTab!.SelectFirstItem();
private void MoveCursorToBottom() => _selectedTab!.SelectLastItem(); private async Task MoveCursorToBottom() => await _selectedTab!.SelectLastItem();
private void ToggleHidden() private async Task ToggleHidden()
{ {
const string hiddenFilterName = "filter_showhiddenelements"; const string hiddenFilterName = "filter_showhiddenelements";
IContainer containerToOpen = _selectedTab!.CurrentLocation; var currentLocation = await _selectedTab!.GetCurrentLocation();
if (_selectedTab.CurrentLocation is VirtualContainer oldVirtualContainer) IContainer containerToOpen = currentLocation;
if (currentLocation is VirtualContainer oldVirtualContainer)
{ {
containerToOpen = oldVirtualContainer.HasWithName(hiddenFilterName) containerToOpen = oldVirtualContainer.HasWithName(hiddenFilterName)
? oldVirtualContainer.ExceptWithName(hiddenFilterName) ? oldVirtualContainer.ExceptWithName(hiddenFilterName)
: GenerateHiddenFilterVirtualContainer(_selectedTab.CurrentLocation); : GenerateHiddenFilterVirtualContainer(currentLocation);
} }
else else
{ {
containerToOpen = GenerateHiddenFilterVirtualContainer(_selectedTab.CurrentLocation); containerToOpen = GenerateHiddenFilterVirtualContainer(currentLocation);
} }
_selectedTab.OpenContainer(containerToOpen); await _selectedTab.OpenContainer(containerToOpen);
static VirtualContainer GenerateHiddenFilterVirtualContainer(IContainer container) static VirtualContainer GenerateHiddenFilterVirtualContainer(IContainer container)
{ {
@@ -70,39 +72,42 @@ namespace FileTime.ConsoleUI.App
} }
} }
public void Select() public async Task Select()
{ {
if (_selectedTab!.CurrentSelectedItem != null) var currentLocation = await _selectedTab!.GetCurrentLocation();
if (currentLocation != null)
{ {
var currentSelectedItem = _selectedTab.CurrentSelectedItem; var currentSelectedItem = await _selectedTab.GetCurrentSelectedItem()!;
if (_paneStates[_selectedTab].ContainsSelectedItem(currentSelectedItem.Provider, _selectedTab.CurrentLocation, currentSelectedItem.FullName!)) if (_paneStates[_selectedTab].ContainsSelectedItem(currentSelectedItem.Provider, currentLocation, currentSelectedItem.FullName!))
{ {
_paneStates[_selectedTab].RemoveSelectedItem(currentSelectedItem.Provider, _selectedTab.CurrentLocation, currentSelectedItem.FullName!); _paneStates[_selectedTab].RemoveSelectedItem(currentSelectedItem.Provider, currentLocation, currentSelectedItem.FullName!);
} }
else else
{ {
_paneStates[_selectedTab].AddSelectedItem(currentSelectedItem.Provider, _selectedTab.CurrentLocation, currentSelectedItem.FullName!); _paneStates[_selectedTab].AddSelectedItem(currentSelectedItem.Provider, currentLocation, currentSelectedItem.FullName!);
} }
_selectedTab.SelectNextItem(); await _selectedTab.SelectNextItem();
} }
} }
public void Copy() public async Task Copy()
{ {
_clipboard.Clear(); _clipboard.Clear();
_clipboard.SetCommand<CopyCommand>(); _clipboard.SetCommand<CopyCommand>();
if (_paneStates[_selectedTab!].GetCurrentSelectedItems().Count > 0) var currentSelectedItems = await _paneStates[_selectedTab!].GetCurrentSelectedItems();
if (currentSelectedItems.Count > 0)
{ {
foreach (var selectedItem in _paneStates[_selectedTab!].GetCurrentSelectedItems()) foreach (var selectedItem in currentSelectedItems)
{ {
_clipboard.AddContent(selectedItem.ContentProvider, selectedItem.Path); _clipboard.AddContent(selectedItem.ContentProvider, selectedItem.Path);
} }
} }
else else
{ {
_clipboard.AddContent(_selectedTab!.CurrentSelectedItem!.Provider, _selectedTab.CurrentSelectedItem.FullName!); var currentSelectedItem = (await _selectedTab!.GetCurrentSelectedItem())!;
_clipboard.AddContent(currentSelectedItem.Provider, currentSelectedItem.FullName!);
} }
} }
@@ -112,21 +117,21 @@ namespace FileTime.ConsoleUI.App
_clipboard.SetCommand<MoveCommand>(); _clipboard.SetCommand<MoveCommand>();
} }
public void PasteMerge() public async Task PasteMerge()
{ {
Paste(TransportMode.Merge); await Paste(TransportMode.Merge);
} }
public void PasteOverwrite() public async Task PasteOverwrite()
{ {
Paste(TransportMode.Overwrite); await Paste(TransportMode.Overwrite);
} }
public void PasteSkip() public async Task PasteSkip()
{ {
Paste(TransportMode.Skip); await Paste(TransportMode.Skip);
} }
private void Paste(TransportMode transportMode) private async Task Paste(TransportMode transportMode)
{ {
if (_clipboard.CommandType != null) if (_clipboard.CommandType != null)
{ {
@@ -140,9 +145,10 @@ namespace FileTime.ConsoleUI.App
command.Sources.Add(item); command.Sources.Add(item);
} }
command.Target = _selectedTab.CurrentLocation is VirtualContainer virtualContainer var currentLocation = await _selectedTab!.GetCurrentLocation();
command.Target = currentLocation is VirtualContainer virtualContainer
? virtualContainer.BaseContainer ? virtualContainer.BaseContainer
: _selectedTab.CurrentLocation; : currentLocation;
_commandExecutor.ExecuteCommand(command); _commandExecutor.ExecuteCommand(command);
@@ -150,24 +156,25 @@ namespace FileTime.ConsoleUI.App
} }
} }
private void CreateContainer() private async Task CreateContainer()
{ {
if (_selectedTab?.CurrentLocation != null) var currentLocation = await _selectedTab?.GetCurrentLocation();
if (currentLocation != null)
{ {
_coloredConsoleRenderer.ResetColor(); _coloredConsoleRenderer.ResetColor();
MoveToIOLine(2); MoveToIOLine(2);
_coloredConsoleRenderer.Write("New container name: "); _coloredConsoleRenderer.Write("New container name: ");
var newContainerName = _consoleReader.ReadText(validator: Validator); var newContainerName = await _consoleReader.ReadText(validator: Validator);
if (!string.IsNullOrWhiteSpace(newContainerName)) if (!string.IsNullOrWhiteSpace(newContainerName))
{ {
_selectedTab.CurrentLocation.CreateContainer(newContainerName); await currentLocation.CreateContainer(newContainerName);
} }
} }
void Validator(string newPath) async Task Validator(string newPath)
{ {
if (_selectedTab!.CurrentLocation.IsExists(newPath)) if (await currentLocation.IsExists(newPath))
{ {
_coloredConsoleRenderer.ForegroundColor = _styles.ErrorColor; _coloredConsoleRenderer.ForegroundColor = _styles.ErrorColor;
} }
@@ -178,30 +185,33 @@ namespace FileTime.ConsoleUI.App
} }
} }
private void HardDelete() private async Task HardDelete()
{ {
IList<IAbsolutePath> itemsToDelete = null; IList<IAbsolutePath>? itemsToDelete = null;
if (_paneStates[_selectedTab!].GetCurrentSelectedItems().Count > 0) var currentSelectedItems = await _paneStates[_selectedTab!].GetCurrentSelectedItems();
var currentSelectedItem = await _selectedTab?.GetCurrentSelectedItem();
if (currentSelectedItems.Count > 0)
{ {
var delete = true; var delete = true;
if (_paneStates[_selectedTab!].GetCurrentSelectedItems().Count == 1 //FIXME: check 'is Container'
&& _paneStates[_selectedTab!].GetCurrentSelectedItems()[0] is IContainer container if (currentSelectedItems.Count == 1
&& container.Items.Count > 0) && currentSelectedItems[0] is IContainer container
&& (await container.GetItems())?.Count > 0)
{ {
delete = AskForApprove($"The container '{container.Name}' is not empty."); delete = AskForApprove($"The container '{container.Name}' is not empty.");
} }
if (delete) if (delete)
{ {
itemsToDelete = _paneStates[_selectedTab].GetCurrentSelectedItems().Cast<IAbsolutePath>().ToList(); itemsToDelete = currentSelectedItems.Cast<IAbsolutePath>().ToList();
} }
} }
else if (_selectedTab?.CurrentSelectedItem != null) else if (currentSelectedItem != null)
{ {
bool delete = true; bool delete = true;
if (_selectedTab?.CurrentSelectedItem is IContainer container && container.Items.Count > 0) if (currentSelectedItem is IContainer container && (await container.GetItems())?.Count > 0)
{ {
delete = AskForApprove($"The container '{container.Name}' is not empty."); delete = AskForApprove($"The container '{container.Name}' is not empty.");
} }
@@ -210,7 +220,7 @@ namespace FileTime.ConsoleUI.App
{ {
itemsToDelete = new List<IAbsolutePath>() itemsToDelete = new List<IAbsolutePath>()
{ {
new AbsolutePath(_selectedTab.CurrentSelectedItem.Provider, _selectedTab.CurrentSelectedItem.FullName!) new AbsolutePath(currentSelectedItem.Provider, currentSelectedItem.FullName!)
}; };
} }
} }

View File

@@ -15,7 +15,7 @@ namespace FileTime.ConsoleUI.App
{ {
public partial class Application public partial class Application
{ {
private readonly List<Tab> _panes = new(); private readonly List<Tab> _tabs = new();
private readonly Dictionary<Tab, Render> _renderers = new(); private readonly Dictionary<Tab, Render> _renderers = new();
private readonly Dictionary<Tab, TabState> _paneStates = new(); private readonly Dictionary<Tab, TabState> _paneStates = new();
private Tab? _selectedTab; private Tab? _selectedTab;
@@ -48,29 +48,30 @@ namespace FileTime.ConsoleUI.App
InitCommandBindings(); InitCommandBindings();
} }
public void SetContainer(IContainer currentPath) public async Task SetContainer(IContainer currentPath)
{ {
_selectedTab = CreateTab(currentPath); _selectedTab = await CreateTab(currentPath);
} }
private Tab CreateTab(IContainer container) private async Task<Tab> CreateTab(IContainer container)
{ {
var pane = new Tab(container); var tab = new Tab();
_panes.Add(pane); await tab.Init(container);
_tabs.Add(tab);
var paneState = new TabState(pane); var paneState = new TabState(tab);
_paneStates.Add(pane, paneState); _paneStates.Add(tab, paneState);
var renderer = _serviceProvider.GetService<Render>()!; var renderer = _serviceProvider.GetService<Render>()!;
renderer.Init(pane, paneState); renderer.Init(tab, paneState);
_renderers.Add(pane, renderer); _renderers.Add(tab, renderer);
return pane; return tab;
} }
private void RemoveTab(Tab pane) private void RemoveTab(Tab pane)
{ {
_panes.Remove(pane); _tabs.Remove(pane);
_renderers.Remove(pane); _renderers.Remove(pane);
_paneStates.Remove(pane); _paneStates.Remove(pane);
} }
@@ -181,15 +182,15 @@ namespace FileTime.ConsoleUI.App
_commandBindings.AddRange(commandBindings); _commandBindings.AddRange(commandBindings);
} }
public void PrintUI() public async Task PrintUI(CancellationToken token = default)
{ {
if (_selectedTab != null) if (_selectedTab != null)
{ {
_renderers[_selectedTab].PrintUI(); await _renderers[_selectedTab].PrintUI(token);
} }
} }
public bool ProcessKey(ConsoleKeyInfo keyinfo) public async Task<bool> ProcessKey(ConsoleKeyInfo keyinfo)
{ {
var key = keyinfo.Key; var key = keyinfo.Key;
_previousKeys.Add(keyinfo); _previousKeys.Add(keyinfo);
@@ -208,7 +209,7 @@ namespace FileTime.ConsoleUI.App
} }
else if (selectedCommandBinding != null) else if (selectedCommandBinding != null)
{ {
selectedCommandBinding.Invoke(); await selectedCommandBinding.InvokeAsync();
_previousKeys.Clear(); _previousKeys.Clear();
} }
else else

View File

@@ -4,14 +4,14 @@ namespace FileTime.ConsoleUI.App.Command
{ {
public class CommandBinding public class CommandBinding
{ {
private readonly Action _commandHandler; private readonly Func<Task> _commandHandler;
public string Name { get; } public string Name { get; }
public ConsoleKeyInfo[] Keys { get; } public ConsoleKeyInfo[] Keys { get; }
public Commands Command { get; } public Commands Command { get; }
public CommandBinding(string name, Commands command, ConsoleKeyInfo[] keys, Action commandHandler) public CommandBinding(string name, Commands command, ConsoleKeyInfo[] keys, Func<Task> commandHandler)
{ {
Name = name; Name = name;
Command = command; Command = command;
@@ -19,6 +19,11 @@ namespace FileTime.ConsoleUI.App.Command
_commandHandler = commandHandler; _commandHandler = commandHandler;
} }
public void Invoke() => _commandHandler(); public CommandBinding(string name, Commands command, ConsoleKeyInfo[] keys, Action commandHandler)
: this(name, command, keys, () => { commandHandler(); return Task.CompletedTask; })
{
}
public async Task InvokeAsync() => await _commandHandler();
} }
} }

View File

@@ -15,7 +15,7 @@ namespace FileTime.ConsoleUI.App.UI
_coloredConsoleRenderer = coloredConsoleRenderer; _coloredConsoleRenderer = coloredConsoleRenderer;
_consoleReader = consoleReader; _consoleReader = consoleReader;
} }
public string?[] ReadInputs(IEnumerable<InputElement> fields) public async Task<string?[]> ReadInputs(IEnumerable<InputElement> fields)
{ {
var results = new List<string?>(); var results = new List<string?>();
@@ -26,7 +26,7 @@ namespace FileTime.ConsoleUI.App.UI
_application.MoveToIOLine(); _application.MoveToIOLine();
_coloredConsoleRenderer.Write(input.Text + ": "); _coloredConsoleRenderer.Write(input.Text + ": ");
results.Add(_consoleReader.ReadText(placeHolder: input.InputType == InputType.Password ? '*' : null)); results.Add(await _consoleReader.ReadText(placeHolder: input.InputType == InputType.Password ? '*' : null));
} }
return results.ToArray(); return results.ToArray();

View File

@@ -10,7 +10,7 @@ namespace FileTime.ConsoleUI.App.UI
{ {
_coloredConsoleRenderer = coloredConsoleRenderer; _coloredConsoleRenderer = coloredConsoleRenderer;
} }
public string? ReadText(int? maxLength = null, Action<string>? validator = null, char? placeHolder = null) public async Task<string?> ReadText(int? maxLength = null, Func<string, Task>? validator = null, char? placeHolder = null)
{ {
var cursorVisible = false; var cursorVisible = false;
try try
@@ -73,7 +73,7 @@ namespace FileTime.ConsoleUI.App.UI
position++; position++;
} }
validator?.Invoke(input); await validator?.Invoke(input);
Console.SetCursorPosition(currentConsoleLeft, currentConsoleTop); Console.SetCursorPosition(currentConsoleLeft, currentConsoleTop);
_coloredConsoleRenderer.Write($"{{0,-{maxLength}}}", placeHolder == null ? input : new string((char)placeHolder, input.Length)); _coloredConsoleRenderer.Write($"{{0,-{maxLength}}}", placeHolder == null ? input : new string((char)placeHolder, input.Length));

View File

@@ -48,33 +48,37 @@ namespace FileTime.ConsoleUI.App.UI
TabState = paneState; TabState = paneState;
} }
public void PrintUI() public async Task PrintUI(CancellationToken token = default)
{ {
if (Tab != null) if (Tab != null)
{ {
PrintPrompt(); await PrintPrompt(token);
PrintTabs(); await PrintTabs(token);
} }
} }
private void PrintTabs() private async Task PrintTabs(CancellationToken token = default)
{ {
var previousColumnWidth = (int)Math.Floor(Console.WindowWidth * 0.15) - 1; var previousColumnWidth = (int)Math.Floor(Console.WindowWidth * 0.15) - 1;
var currentColumnWidth = (int)Math.Floor(Console.WindowWidth * 0.4) - 1; var currentColumnWidth = (int)Math.Floor(Console.WindowWidth * 0.4) - 1;
var nextColumnWidth = Console.WindowWidth - currentColumnWidth - previousColumnWidth - 2; var nextColumnWidth = Console.WindowWidth - currentColumnWidth - previousColumnWidth - 2;
var currentVirtualContainer = Tab!.CurrentLocation as VirtualContainer;
if (Tab.CurrentLocation.GetParent() is var parentContainer && parentContainer is not null) var currentLocation = (await Tab!.GetCurrentLocation())!;
var currentSelectedItem = (await Tab!.GetCurrentSelectedItem())!;
var currentVirtualContainer = currentLocation as VirtualContainer;
if (currentLocation.GetParent() is var parentContainer && parentContainer is not null)
{ {
parentContainer.Refresh(); await parentContainer.Refresh();
PrintColumn( await PrintColumn(
currentVirtualContainer != null currentVirtualContainer != null
? currentVirtualContainer.CloneVirtualChainFor(parentContainer, v => v.IsTransitive) ? currentVirtualContainer.CloneVirtualChainFor(parentContainer, v => v.IsTransitive)
: parentContainer, : parentContainer,
currentVirtualContainer != null currentVirtualContainer != null
? currentVirtualContainer.GetRealContainer() ? currentVirtualContainer.GetRealContainer()
: Tab.CurrentLocation, : currentLocation,
PrintMode.Previous, PrintMode.Previous,
0, 0,
_contentPaddingTop, _contentPaddingTop,
@@ -90,29 +94,35 @@ namespace FileTime.ConsoleUI.App.UI
_contentRowCount); _contentRowCount);
} }
Tab.CurrentLocation.Refresh(); if (token.IsCancellationRequested) return;
CheckAndSetCurrentDisplayStartY(); await currentLocation.Refresh();
PrintColumn(
Tab.CurrentLocation, await CheckAndSetCurrentDisplayStartY();
Tab.CurrentSelectedItem, await PrintColumn(
currentLocation,
currentSelectedItem,
PrintMode.Current, PrintMode.Current,
previousColumnWidth + 1, previousColumnWidth + 1,
_contentPaddingTop, _contentPaddingTop,
currentColumnWidth, currentColumnWidth,
_contentRowCount); _contentRowCount);
if (Tab.CurrentSelectedItem is IContainer selectedContainer) if (token.IsCancellationRequested) return;
if (currentSelectedItem is IContainer selectedContainer)
{ {
selectedContainer.Refresh(); await selectedContainer.Refresh();
selectedContainer = currentVirtualContainer != null selectedContainer = currentVirtualContainer != null
? currentVirtualContainer.CloneVirtualChainFor(selectedContainer, v => v.IsTransitive) ? currentVirtualContainer.CloneVirtualChainFor(selectedContainer, v => v.IsTransitive)
: selectedContainer; : selectedContainer;
PrintColumn( var selectedContainerItems = (await selectedContainer.GetItems())!;
await PrintColumn(
selectedContainer, selectedContainer,
selectedContainer.Items.Count > 0 ? selectedContainer.Items[0] : null, selectedContainerItems.Count > 0 ? selectedContainerItems[0] : null,
PrintMode.Next, PrintMode.Next,
previousColumnWidth + currentColumnWidth + 2, previousColumnWidth + currentColumnWidth + 2,
_contentPaddingTop, _contentPaddingTop,
@@ -129,8 +139,11 @@ namespace FileTime.ConsoleUI.App.UI
} }
} }
private void PrintPrompt() private async Task PrintPrompt(CancellationToken token = default)
{ {
var currentLocation = await Tab!.GetCurrentLocation();
var currentSelectedItem = await Tab!.GetCurrentSelectedItem();
Console.SetCursorPosition(0, 0); Console.SetCursorPosition(0, 0);
_coloredRenderer.ResetColor(); _coloredRenderer.ResetColor();
_coloredRenderer.ForegroundColor = _appStyle.AccentForeground; _coloredRenderer.ForegroundColor = _appStyle.AccentForeground;
@@ -140,24 +153,24 @@ namespace FileTime.ConsoleUI.App.UI
_coloredRenderer.Write(' '); _coloredRenderer.Write(' ');
_coloredRenderer.ForegroundColor = _appStyle.ContainerForeground; _coloredRenderer.ForegroundColor = _appStyle.ContainerForeground;
var path = Tab!.CurrentLocation.FullName + "/"; var path = currentLocation.FullName + "/";
_coloredRenderer.Write(path); _coloredRenderer.Write(path);
if (Tab.CurrentSelectedItem?.Name != null) if (currentSelectedItem?.Name != null)
{ {
_coloredRenderer.ResetColor(); _coloredRenderer.ResetColor();
_coloredRenderer.Write($"{{0,-{300 - path.Length}}}", Tab.CurrentSelectedItem.Name); _coloredRenderer.Write($"{{0,-{300 - path.Length}}}", currentSelectedItem.Name);
} }
} }
private void PrintColumn(IContainer currentContainer, IItem? currentItem, PrintMode printMode, int startX, int startY, int elementWidth, int availableRows) private async Task PrintColumn(IContainer currentContainer, IItem? currentItem, PrintMode printMode, int startX, int startY, int elementWidth, int availableRows, CancellationToken token = default)
{ {
var allItem = currentContainer.Containers.Cast<IItem>().Concat(currentContainer.Elements).ToList(); var allItem = (await currentContainer.GetItems())!.ToList();
var printedItemsCount = 0; var printedItemsCount = 0;
var currentY = 0; var currentY = 0;
if (allItem.Count > 0) if (allItem.Count > 0)
{ {
var currentIndex = allItem.FindIndex(i => i == currentItem); var currentIndex = allItem.FindIndex(i => i.Name == currentItem?.Name);
var skipElements = printMode switch var skipElements = printMode switch
{ {
@@ -169,7 +182,7 @@ namespace FileTime.ConsoleUI.App.UI
var maxTextWidth = elementWidth - ITEMPADDINGLEFT - ITEMPADDINGRIGHT; var maxTextWidth = elementWidth - ITEMPADDINGLEFT - ITEMPADDINGRIGHT;
var itemsToPrint = currentContainer.Items.Skip(skipElements).Take(availableRows).ToList(); var itemsToPrint = (await currentContainer.GetItems())!.Skip(skipElements).Take(availableRows).ToList();
printedItemsCount = itemsToPrint.Count; printedItemsCount = itemsToPrint.Count;
foreach (var item in itemsToPrint) foreach (var item in itemsToPrint)
{ {
@@ -182,7 +195,7 @@ namespace FileTime.ConsoleUI.App.UI
var container = item as IContainer; var container = item as IContainer;
var element = item as IElement; var element = item as IElement;
attributePart = container != null ? "" + container.Items.Count : element!.GetPrimaryAttributeText(); attributePart = container != null ? "" + (await container.GetItems())!.Count : element!.GetPrimaryAttributeText();
IConsoleColor? backgroundColor = null; IConsoleColor? backgroundColor = null;
IConsoleColor? foregroundColor = null; IConsoleColor? foregroundColor = null;
@@ -213,7 +226,7 @@ namespace FileTime.ConsoleUI.App.UI
foregroundColor = _appStyle.SelectedItemForeground; foregroundColor = _appStyle.SelectedItemForeground;
} }
if (item == currentItem) if (item.Name == currentItem?.Name)
{ {
(backgroundColor, foregroundColor) = (foregroundColor, backgroundColor); (backgroundColor, foregroundColor) = (foregroundColor, backgroundColor);
} }
@@ -224,6 +237,7 @@ namespace FileTime.ConsoleUI.App.UI
var text = string.Format($"{{0,-{elementWidth}}}", _paddingLeft + (isSelected ? " " : "") + namePart + _paddingRight); var text = string.Format($"{{0,-{elementWidth}}}", _paddingLeft + (isSelected ? " " : "") + namePart + _paddingRight);
text = string.Concat(text.AsSpan(0, text.Length - attributePart.Length - 1), " ", attributePart); text = string.Concat(text.AsSpan(0, text.Length - attributePart.Length - 1), " ", attributePart);
if (token.IsCancellationRequested) return;
Console.SetCursorPosition(startX, startY + currentY++); Console.SetCursorPosition(startX, startY + currentY++);
_coloredRenderer.Write(text); _coloredRenderer.Write(text);
@@ -262,7 +276,7 @@ namespace FileTime.ConsoleUI.App.UI
} }
} }
private void CheckAndSetCurrentDisplayStartY() private async Task CheckAndSetCurrentDisplayStartY()
{ {
const int padding = 5; const int padding = 5;
@@ -273,7 +287,7 @@ namespace FileTime.ConsoleUI.App.UI
} }
while (Tab.CurrentSelectedIndex > _currentDisplayStartY + _contentRowCount - padding while (Tab.CurrentSelectedIndex > _currentDisplayStartY + _contentRowCount - padding
&& _currentDisplayStartY < Tab.CurrentLocation.Items.Count - _contentRowCount) && _currentDisplayStartY < (await (await Tab.GetCurrentLocation()).GetItems())!.Count - _contentRowCount)
{ {
_currentDisplayStartY++; _currentDisplayStartY++;
} }

View File

@@ -0,0 +1,44 @@
namespace FileTime.ConsoleUI.App.UI
{
public class RenderSynchronizer
{
private readonly object renderLock = new();
private readonly Application _application;
private bool _needsRender;
private CancellationTokenSource renderCancellationSource = new();
public RenderSynchronizer(Application application)
{
_application = application;
}
public async void Start()
{
while (_application.IsRunning)
{
if (_needsRender)
{
lock (renderLock)
{
_needsRender = false;
renderCancellationSource = new();
}
await _application.PrintUI(renderCancellationSource.Token);
}
}
}
public void NeedsReRender()
{
lock (renderLock)
{
_needsRender = true;
if (renderCancellationSource.Token.CanBeCanceled)
{
renderCancellationSource.Cancel();
}
}
}
}
}

View File

@@ -15,63 +15,83 @@ namespace FileTime.ConsoleUI
public class Program public class Program
{ {
static ILogger<Program>? _logger; static ILogger<Program>? _logger;
static Thread? _renderThread;
public static void Main() public static async Task Main()
{ {
var serviceProvider = CreateServiceProvider(); var serviceProvider = CreateServiceProvider();
_logger = serviceProvider.GetService<ILogger<Program>>()!; _logger = serviceProvider.GetService<ILogger<Program>>()!;
var coloredConsoleRenderer = serviceProvider.GetService<IColoredConsoleRenderer>()!; var coloredConsoleRenderer = serviceProvider.GetService<IColoredConsoleRenderer>()!;
var localContentProvider = serviceProvider.GetService<LocalContentProvider>()!; var localContentProvider = serviceProvider.GetService<LocalContentProvider>()!;
var renderSynchronizer = serviceProvider.GetService<RenderSynchronizer>()!;
var currentPath = Environment.CurrentDirectory.Replace(Path.DirectorySeparatorChar, Constants.SeparatorChar); var currentPath = Environment.CurrentDirectory.Replace(Path.DirectorySeparatorChar, Constants.SeparatorChar);
_logger.LogInformation("Current directory: '{0}'", currentPath); _logger.LogInformation("Current directory: '{0}'", currentPath);
var currentPossibleDirectory = localContentProvider.GetByPath(currentPath); var currentPossibleDirectory = await localContentProvider.GetByPath(currentPath);
if (currentPossibleDirectory is IContainer container) if (currentPossibleDirectory is IContainer container)
{ {
serviceProvider.GetService<TopContainer>(); serviceProvider.GetService<TopContainer>();
coloredConsoleRenderer.Clear(); coloredConsoleRenderer.Clear();
Console.CursorVisible = false; try
{
Console.CursorVisible = false;
}
catch { }
var app = serviceProvider.GetService<Application>()!; var app = serviceProvider.GetService<Application>()!;
app.SetContainer(container); await app.SetContainer(container);
app.PrintUI(); renderSynchronizer.NeedsReRender();
_renderThread = new Thread(new ThreadStart(renderSynchronizer.Start));
_renderThread.Start();
while (app.IsRunning) while (app.IsRunning)
{ {
if (app.ProcessKey(Console.ReadKey(true))) if (await app.ProcessKey(Console.ReadKey(true)))
{ {
app.PrintUI(); renderSynchronizer.NeedsReRender();
} }
} }
renderSynchronizer.NeedsReRender();
Console.SetCursorPosition(0, Console.WindowHeight - 1); Console.SetCursorPosition(0, Console.WindowHeight - 1);
Console.CursorVisible = true; Console.CursorVisible = true;
} }
else else
{ {
Console.WriteLine("Current working directory is not a directory???"); Console.WriteLine("Current working directory is not a directory??? Possible directory's type is: '" + currentPossibleDirectory?.GetType() + "'");
Thread.Sleep(100); Thread.Sleep(100);
} }
} }
private static bool IsAnsiColorSupported() private static bool IsAnsiColorSupported()
{ {
Console.CursorLeft = 0; try
Console.CursorTop = 0; {
Console.CursorLeft = 0;
Console.CursorTop = 0;
Console.Write("\u001b[0ma"); Console.Write("\u001b[0ma");
return Console.CursorLeft == 1 && Console.CursorTop == 0; return Console.CursorLeft == 1 && Console.CursorTop == 0;
}
catch
{
return false;
}
} }
private static ServiceProvider CreateServiceProvider() private static ServiceProvider CreateServiceProvider()
{ {
return DependencyInjection.RegisterDefaultServices() return DependencyInjection.RegisterDefaultServices()
.AddLogging(/* (builder) => builder.AddConsole().AddDebug() */) //.AddLogging()
.AddLogging((builder) => builder.AddConsole().AddDebug())
.AddSingleton<Application>() .AddSingleton<Application>()
.AddSingleton<RenderSynchronizer>()
.AddSingleton<IStyles>(new Styles(IsAnsiColorSupported())) .AddSingleton<IStyles>(new Styles(IsAnsiColorSupported()))
.AddSingleton<IColoredConsoleRenderer, ColoredConsoleRenderer>() .AddSingleton<IColoredConsoleRenderer, ColoredConsoleRenderer>()
.AddSingleton<ConsoleReader>() .AddSingleton<ConsoleReader>()

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,7 @@
namespace AsyncEvent
{
public class AsyncEventArgs
{
public static AsyncEventArgs Empty = new AsyncEventArgs();
}
}

View File

@@ -0,0 +1,68 @@
namespace AsyncEvent
{
public class AsyncEventHandler<TSender, TArg> where TArg : AsyncEventArgs
{
private readonly List<Func<TSender, TArg, Task>> _handlers;
private readonly Action<Func<TSender, TArg, Task>> _add;
private readonly Action<Func<TSender, TArg, Task>> _remove;
public IReadOnlyList<Func<TSender, TArg, Task>> Handlers { get; }
public AsyncEventHandler(Action<Func<TSender, TArg, Task>>? add = null, Action<Func<TSender, TArg, Task>>? remove = null)
{
_handlers = new List<Func<TSender, TArg, Task>>();
Handlers = _handlers.AsReadOnly();
_add = add ?? AddInternal;
_remove = remove ?? RemoveInternal;
}
public void Add(Func<TSender, TArg, Task> handler)
{
_add.Invoke(handler);
}
public void Remove(Func<TSender, TArg, Task> handler)
{
_remove.Invoke(handler);
}
private void AddInternal(Func<TSender, TArg, Task> handler)
{
_handlers.Add(handler);
}
private void RemoveInternal(Func<TSender, TArg, Task> handler)
{
_handlers.Remove(handler);
}
public async Task InvokeAsync(TSender sender, TArg args)
{
foreach(var handler in _handlers)
{
await handler(sender, args);
}
}
public static AsyncEventHandler<TSender, TArg> operator +(AsyncEventHandler<TSender, TArg> obj, Func<TSender, TArg, Task> handler)
{
obj.Add(handler);
return obj;
}
public static AsyncEventHandler<TSender, TArg> operator -(AsyncEventHandler<TSender, TArg> obj, Func<TSender, TArg, Task> handler)
{
obj.Remove(handler);
return obj;
}
}
public class AsyncEventHandler<TArg> : AsyncEventHandler<object?, AsyncEventArgs> where TArg : AsyncEventArgs
{
public AsyncEventHandler(Action<Func<object?, TArg, Task>>? add = null, Action<Func<object?, TArg, 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) { }
}
}

View File

@@ -16,38 +16,39 @@ namespace FileTime.Core.Command
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void Execute(Action<IAbsolutePath, IAbsolutePath> copy) public async Task Execute(Action<IAbsolutePath, IAbsolutePath> copy)
{ {
DoCopy(Sources, Target, TransportMode, copy); await DoCopy(Sources, Target, TransportMode, copy);
} }
private void DoCopy(IEnumerable<IAbsolutePath> sources, IContainer target, TransportMode transportMode, Action<IAbsolutePath, IAbsolutePath> copy) private async Task DoCopy(IEnumerable<IAbsolutePath> sources, IContainer target, TransportMode transportMode, Action<IAbsolutePath, IAbsolutePath> copy)
{ {
foreach (var source in sources) foreach (var source in sources)
{ {
var item = source.ContentProvider.GetByPath(source.Path); var item = await source.ContentProvider.GetByPath(source.Path);
if (item is IContainer container) if (item is IContainer container)
{ {
var targetContainer = target.Containers.FirstOrDefault(d => d.Name == container.Name) ?? (target.CreateContainer(container.Name)!); var targetContainer = (await target.GetContainers())?.FirstOrDefault(d => d.Name == container.Name) ?? (await target.CreateContainer(container.Name)!);
var childDirectories = container.Containers.Select(d => new AbsolutePath(item.Provider, d.FullName!)); var childDirectories = (await container.GetContainers())!.Select(d => new AbsolutePath(item.Provider, d.FullName!));
var childFiles = container.Elements.Select(f => new AbsolutePath(item.Provider, f.FullName!)); var childFiles = (await container.GetElements())!.Select(f => new AbsolutePath(item.Provider, f.FullName!));
DoCopy(childDirectories.Concat(childFiles), targetContainer, transportMode, copy); await DoCopy(childDirectories.Concat(childFiles), targetContainer, transportMode, copy);
} }
else if (item is IElement element) else if (item is IElement element)
{ {
var targetName = element.Name; var targetName = element.Name;
var targetNameExists = await target.IsExists(targetName);
if (transportMode == TransportMode.Merge) if (transportMode == TransportMode.Merge)
{ {
for (var i = 0; target.IsExists(targetName); i++) for (var i = 0; targetNameExists; i++)
{ {
targetName = element.Name + (i == 0 ? "_" : $"_{i}"); targetName = element.Name + (i == 0 ? "_" : $"_{i}");
} }
} }
else if (transportMode == TransportMode.Skip && target.IsExists(targetName)) else if (transportMode == TransportMode.Skip && targetNameExists)
{ {
continue; continue;
} }

View File

@@ -12,29 +12,29 @@ namespace FileTime.Core.Command
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void Execute() public async Task Execute()
{ {
foreach (var item in ItemsToDelete) foreach (var item in ItemsToDelete)
{ {
DoDelete(item.ContentProvider.GetByPath(item.Path)!); await DoDelete(await item.ContentProvider.GetByPath(item.Path)!);
} }
} }
private void DoDelete(IItem item) private async Task DoDelete(IItem item)
{ {
if (item is IContainer container) if (item is IContainer container)
{ {
foreach (var child in container.Items) foreach (var child in await container.GetItems())
{ {
DoDelete(child); await DoDelete(child);
child.Delete(); await child.Delete();
} }
item.Delete(); await item.Delete();
} }
else if(item is IElement element) else if(item is IElement element)
{ {
element.Delete(); await element.Delete();
} }
} }
} }

View File

@@ -2,6 +2,6 @@ namespace FileTime.Core.Command
{ {
public interface IExecutableCommand : ICommand public interface IExecutableCommand : ICommand
{ {
void Execute(); Task Execute();
} }
} }

View File

@@ -1,3 +1,4 @@
using AsyncEvent;
using FileTime.Core.Models; using FileTime.Core.Models;
namespace FileTime.Core.Components namespace FileTime.Core.Components
@@ -7,7 +8,7 @@ namespace FileTime.Core.Components
private IItem? _currentSelectedItem; private IItem? _currentSelectedItem;
private IContainer _currentLocation; private IContainer _currentLocation;
public IContainer CurrentLocation /* public IContainer CurrentLocation
{ {
get => _currentLocation; get => _currentLocation;
private set private set
@@ -38,75 +39,122 @@ namespace FileTime.Core.Components
CurrentSelectedItemChanged?.Invoke(this, EventArgs.Empty); CurrentSelectedItemChanged?.Invoke(this, EventArgs.Empty);
} }
} }
} } */
public int CurrentSelectedIndex { get; private set; } public int CurrentSelectedIndex { get; private set; }
public event EventHandler CurrentLocationChanged; public event EventHandler CurrentLocationChanged;
public event EventHandler CurrentSelectedItemChanged; public event EventHandler CurrentSelectedItemChanged;
public Tab(IContainer currentPath) public async Task Init(IContainer currentPath)
{ {
CurrentLocation = currentPath; await SetCurrentLocation(currentPath);
CurrentSelectedItem = CurrentLocation.Items.Count > 0 ? CurrentLocation.Items[0] : null;
} }
private void HandleCurrentLocationRefresh(object? sender, EventArgs e) public Task<IContainer> GetCurrentLocation()
{ {
var currentSelectedName = CurrentSelectedItem?.FullName; return Task.FromResult(_currentLocation);
}
public async Task SetCurrentLocation(IContainer value)
{
if (_currentLocation != value)
{
if (_currentLocation != null)
{
_currentLocation.Refreshed.Remove(HandleCurrentLocationRefresh);
}
_currentLocation = value;
CurrentLocationChanged?.Invoke(this, EventArgs.Empty);
var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
await SetCurrentSelectedItem(currentLocationItems.Count > 0 ? currentLocationItems[0] : null);
_currentLocation.Refreshed.Add(HandleCurrentLocationRefresh);
}
}
public Task<IItem?> GetCurrentSelectedItem()
{
return Task.FromResult(_currentSelectedItem);
}
public async Task SetCurrentSelectedItem(IItem? value)
{
if (_currentSelectedItem != value)
{
var contains = (await _currentLocation.GetItems())?.Contains(value) ?? false;
if(value != null && !contains) throw new IndexOutOfRangeException("Provided item does not exists in the current container.");
_currentSelectedItem = value;
CurrentSelectedIndex = await GetItemIndex(value);
CurrentSelectedItemChanged?.Invoke(this, EventArgs.Empty);
}
}
private async Task HandleCurrentLocationRefresh(object? sender, AsyncEventArgs e)
{
var currentSelectedName = (await GetCurrentSelectedItem())?.FullName;
var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
if (currentSelectedName != null) if (currentSelectedName != null)
{ {
CurrentSelectedItem = CurrentLocation.Items.FirstOrDefault(i => i.FullName == currentSelectedName) ?? _currentLocation.Items.FirstOrDefault(); await SetCurrentSelectedItem(currentLocationItems.FirstOrDefault(i => i.FullName == currentSelectedName) ?? currentLocationItems.FirstOrDefault());
} }
else if (CurrentLocation.Items.Count > 0) else if (currentLocationItems.Count > 0)
{ {
CurrentSelectedItem = CurrentLocation.Items[0]; await SetCurrentSelectedItem(currentLocationItems[0]);
} }
} }
public void SelectFirstItem() public async Task SelectFirstItem()
{ {
if (CurrentLocation.Items.Count > 0) var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
if (currentLocationItems.Count > 0)
{ {
CurrentSelectedItem = CurrentLocation.Items[0]; await SetCurrentSelectedItem(currentLocationItems[0]);
} }
} }
public void SelectLastItem() public async Task SelectLastItem()
{ {
if (CurrentLocation.Items.Count > 0) var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
if (currentLocationItems.Count > 0)
{ {
CurrentSelectedItem = CurrentLocation.Items[CurrentLocation.Items.Count - 1]; await SetCurrentSelectedItem(currentLocationItems[currentLocationItems.Count - 1]);
} }
} }
public void SelectPreviousItem(int skip = 0) public async Task SelectPreviousItem(int skip = 0)
{ {
var possibleItemsToSelect = CurrentLocation.Items.Take(CurrentSelectedIndex).Reverse().Skip(skip).ToList(); var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
var possibleItemsToSelect = currentLocationItems.Take(CurrentSelectedIndex).Reverse().Skip(skip).ToList();
if (possibleItemsToSelect.Count == 0) possibleItemsToSelect = CurrentLocation.Items.ToList(); if (possibleItemsToSelect.Count == 0) possibleItemsToSelect = currentLocationItems.ToList();
SelectItem(possibleItemsToSelect); await SelectItem(possibleItemsToSelect);
} }
public void SelectNextItem(int skip = 0) public async Task SelectNextItem(int skip = 0)
{ {
var possibleItemsToSelect = CurrentLocation.Items.Skip(CurrentSelectedIndex + 1 + skip).ToList(); var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
var possibleItemsToSelect = currentLocationItems.Skip(CurrentSelectedIndex + 1 + skip).ToList();
if (possibleItemsToSelect.Count == 0) possibleItemsToSelect = CurrentLocation.Items.Reverse().ToList(); if (possibleItemsToSelect.Count == 0) possibleItemsToSelect = currentLocationItems.Reverse().ToList();
SelectItem(possibleItemsToSelect); await SelectItem(possibleItemsToSelect);
} }
private void SelectItem(IEnumerable<IItem> currentPossibleItems) private async Task SelectItem(IEnumerable<IItem> currentPossibleItems)
{ {
if (!currentPossibleItems.Any()) return; if (!currentPossibleItems.Any()) return;
if (CurrentSelectedItem != null) var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
if (await GetCurrentSelectedItem() != null)
{ {
CurrentLocation.Refresh(); (await GetCurrentLocation())?.Refresh();
IItem? newSelectedItem = null; IItem? newSelectedItem = null;
foreach (var item in currentPossibleItems) foreach (var item in currentPossibleItems)
{ {
if (CurrentLocation.Items.FirstOrDefault(i => i.Name == item.Name) is var possibleNewSelectedItem if (currentLocationItems.FirstOrDefault(i => i.Name == item.Name) is var possibleNewSelectedItem
&& possibleNewSelectedItem is not null) && possibleNewSelectedItem is not null)
{ {
newSelectedItem = possibleNewSelectedItem; newSelectedItem = possibleNewSelectedItem;
@@ -114,78 +162,89 @@ namespace FileTime.Core.Components
} }
} }
CurrentSelectedItem = newSelectedItem ?? (CurrentLocation.Items.Count > 0 ? CurrentLocation.Items[0] : null); if(newSelectedItem != null)
{
newSelectedItem = (await (await GetCurrentLocation()).GetItems())?.FirstOrDefault(i => i.Name == newSelectedItem.Name);
}
await SetCurrentSelectedItem(newSelectedItem ?? (currentLocationItems.Count > 0 ? currentLocationItems[0] : null));
} }
else else
{ {
CurrentSelectedItem = CurrentLocation.Items.Count > 0 ? CurrentLocation.Items[0] : null; await SetCurrentSelectedItem(currentLocationItems.Count > 0 ? currentLocationItems[0] : null);
} }
} }
public void GoToProvider() public async Task GoToProvider()
{ {
if (CurrentLocation == null) return; var currentLocatin = await GetCurrentLocation();
if (currentLocatin == null) return;
CurrentLocation = CurrentLocation.Provider; await SetCurrentLocation(currentLocatin.Provider);
} }
public void GoToRoot() public async Task GoToRoot()
{ {
if (CurrentLocation == null) return; var currentLocatin = await GetCurrentLocation();
if (currentLocatin == null) return;
var root = CurrentLocation; var root = currentLocatin;
while (root!.GetParent() != null) while (root!.GetParent() != null)
{ {
root = root.GetParent(); root = root.GetParent();
} }
CurrentLocation = root; await SetCurrentLocation(root);
} }
public void GoUp() public async Task GoUp()
{ {
var lastCurrentLocation = CurrentLocation; var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
var parent = CurrentLocation.GetParent(); var lastCurrentLocation = await GetCurrentLocation();
var parent = (await GetCurrentLocation()).GetParent();
if (parent is not null) if (parent is not null)
{ {
if (lastCurrentLocation is VirtualContainer lastCurrentVirtualContainer) if (lastCurrentLocation is VirtualContainer lastCurrentVirtualContainer)
{ {
CurrentLocation = lastCurrentVirtualContainer.CloneVirtualChainFor(parent, v => v.IsPermanent); await SetCurrentLocation(lastCurrentVirtualContainer.CloneVirtualChainFor(parent, v => v.IsPermanent));
CurrentSelectedItem = lastCurrentVirtualContainer.GetRealContainer(); await SetCurrentSelectedItem(lastCurrentVirtualContainer.GetRealContainer());
} }
else else
{ {
CurrentLocation = parent; await SetCurrentLocation(parent);
CurrentSelectedItem = lastCurrentLocation; var newCurrentLocation = (await (await GetCurrentLocation()).GetItems())?.FirstOrDefault(i => i.Name == lastCurrentLocation.Name);
await SetCurrentSelectedItem(newCurrentLocation);
} }
} }
} }
public void Open() public async Task Open()
{ {
var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
if (_currentSelectedItem is IContainer childContainer) if (_currentSelectedItem is IContainer childContainer)
{ {
if (CurrentLocation is VirtualContainer currentVirtuakContainer) if (await GetCurrentLocation() is VirtualContainer currentVirtuakContainer)
{ {
CurrentLocation = currentVirtuakContainer.CloneVirtualChainFor(childContainer, v => v.IsPermanent); await SetCurrentLocation(currentVirtuakContainer.CloneVirtualChainFor(childContainer, v => v.IsPermanent));
} }
else else
{ {
CurrentLocation = childContainer; await SetCurrentLocation(childContainer);
} }
} }
} }
public void OpenContainer(IContainer container) => CurrentLocation = container; public async Task OpenContainer(IContainer container) => await SetCurrentLocation(container);
private int GetItemIndex(IItem? item) private async Task<int> GetItemIndex(IItem? item)
{ {
if (item == null) return -1; if (item == null) return -1;
var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
for (var i = 0; i < CurrentLocation.Items.Count; i++) for (var i = 0; i < currentLocationItems.Count; i++)
{ {
if (CurrentLocation.Items[i] == item) return i; if (currentLocationItems[i] == item) return i;
} }
return -1; return -1;

View File

@@ -2,6 +2,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\AppCommon\FileTime.App.Style\FileTime.App.Style.csproj" /> <ProjectReference Include="..\..\AppCommon\FileTime.App.Style\FileTime.App.Style.csproj" />
<ProjectReference Include="..\AsyncEvent\AsyncEvent.csproj" />
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>

View File

@@ -2,6 +2,6 @@ namespace FileTime.Core.Interactions
{ {
public interface IInputInterface public interface IInputInterface
{ {
string?[] ReadInputs(IEnumerable<InputElement> fields); Task<string?[]> ReadInputs(IEnumerable<InputElement> fields);
} }
} }

View File

@@ -1,19 +1,21 @@
using AsyncEvent;
namespace FileTime.Core.Models namespace FileTime.Core.Models
{ {
public interface IContainer : IItem public interface IContainer : IItem
{ {
IReadOnlyList<IItem> Items { get; } Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default);
IReadOnlyList<IContainer> Containers { get; } Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default);
IReadOnlyList<IElement> Elements { get; } Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default);
void Refresh(); Task Refresh();
IContainer? GetParent(); IContainer? GetParent();
IItem? GetByPath(string path); Task<IItem?> GetByPath(string path);
IContainer CreateContainer(string name); Task<IContainer> CreateContainer(string name);
IElement CreateElement(string name); Task<IElement> CreateElement(string name);
bool IsExists(string name); Task<bool> IsExists(string name);
event EventHandler? Refreshed; AsyncEventHandler Refreshed { get; }
} }
} }

View File

@@ -8,6 +8,6 @@ namespace FileTime.Core.Models
string? FullName { get; } string? FullName { get; }
bool IsHidden { get; } bool IsHidden { get; }
IContentProvider Provider { get; } IContentProvider Provider { get; }
void Delete(); Task Delete();
} }
} }

View File

@@ -1,3 +1,4 @@
using AsyncEvent;
using FileTime.Core.Providers; using FileTime.Core.Providers;
namespace FileTime.Core.Models namespace FileTime.Core.Models
@@ -12,11 +13,11 @@ namespace FileTime.Core.Models
public bool IsPermanent { get; } public bool IsPermanent { get; }
public bool IsTransitive { get; } public bool IsTransitive { get; }
public string? VirtualContainerName { get; } public string? VirtualContainerName { get; }
public IReadOnlyList<IItem> Items { get; private set; } public IReadOnlyList<IItem>? Items { get; private set; }
public IReadOnlyList<IContainer> Containers { get; private set; } public IReadOnlyList<IContainer>? Containers { get; private set; }
public IReadOnlyList<IElement> Elements { get; private set; } public IReadOnlyList<IElement>? Elements { get; private set; }
public string Name => BaseContainer.Name; public string Name => BaseContainer.Name;
@@ -26,10 +27,15 @@ namespace FileTime.Core.Models
public IContentProvider Provider => BaseContainer.Provider; public IContentProvider Provider => BaseContainer.Provider;
public event EventHandler? Refreshed public AsyncEventHandler Refreshed { get; }
private void RefreshAddBase(Func<object?, AsyncEventArgs, Task> handler)
{ {
add => BaseContainer.Refreshed += value; BaseContainer.Refreshed.Add(handler);
remove => BaseContainer.Refreshed -= value; }
private void RefreshRemoveBase(Func<object?, AsyncEventArgs, Task> handler)
{
BaseContainer.Refreshed.Add(handler);
} }
public VirtualContainer( public VirtualContainer(
@@ -40,32 +46,40 @@ namespace FileTime.Core.Models
bool isTransitive = false, bool isTransitive = false,
string? virtualContainerName = null) string? virtualContainerName = null)
{ {
Refreshed = new (RefreshAddBase, RefreshRemoveBase);
BaseContainer = baseContainer; BaseContainer = baseContainer;
_containerTransformators = containerTransformators; _containerTransformators = containerTransformators;
_elementTransformators = elementTransformators; _elementTransformators = elementTransformators;
InitItems();
IsPermanent = isPermanent; IsPermanent = isPermanent;
IsTransitive = isTransitive; IsTransitive = isTransitive;
VirtualContainerName = virtualContainerName; VirtualContainerName = virtualContainerName;
} }
private void InitItems() public async Task Init()
{ {
Containers = _containerTransformators.Aggregate(BaseContainer.Containers.AsEnumerable(), (a, t) => t(a)).ToList().AsReadOnly(); await InitItems();
Elements = _elementTransformators.Aggregate(BaseContainer.Elements.AsEnumerable(), (a, t) => t(a)).ToList().AsReadOnly();
Items = Containers.Cast<IItem>().Concat(Elements).ToList().AsReadOnly();
} }
public IItem? GetByPath(string path) => BaseContainer.GetByPath(path); private async Task InitItems()
{
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();
Items = (Elements != null
? Containers?.Cast<IItem>().Concat(Elements)
: Containers?.Cast<IItem>())
?.ToList().AsReadOnly();
}
public async Task<IItem?> GetByPath(string path) => await BaseContainer.GetByPath(path);
public IContainer? GetParent() => BaseContainer.GetParent(); public IContainer? GetParent() => BaseContainer.GetParent();
public void Refresh() public async Task Refresh()
{ {
BaseContainer.Refresh(); await BaseContainer.Refresh();
InitItems(); await InitItems();
} }
public IContainer GetRealContainer() => public IContainer GetRealContainer() =>
@@ -113,10 +127,23 @@ namespace FileTime.Core.Models
: baseContainer; : baseContainer;
} }
public IContainer CreateContainer(string name) => BaseContainer.CreateContainer(name); public async Task<IContainer> CreateContainer(string name) => await BaseContainer.CreateContainer(name);
public IElement CreateElement(string name) => BaseContainer.CreateElement(name); public async Task<IElement> CreateElement(string name) => await BaseContainer.CreateElement(name);
public bool IsExists(string name) => BaseContainer.IsExists(name); public async Task<bool> IsExists(string name) => await BaseContainer.IsExists(name);
public void Delete() => BaseContainer.Delete(); public Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
{
return Task.FromResult(Items);
}
public Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
{
return Task.FromResult(Containers);
}
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default)
{
return Task.FromResult(Elements);
}
public async Task Delete() => await BaseContainer.Delete();
} }
} }

View File

@@ -4,7 +4,7 @@ namespace FileTime.Core.Providers
{ {
public interface IContentProvider : IContainer public interface IContentProvider : IContainer
{ {
IReadOnlyList<IContainer> RootContainers { get; } Task<IReadOnlyList<IContainer>> GetRootContainers(CancellationToken token = default);
bool CanHandlePath(string path); bool CanHandlePath(string path);

View File

@@ -1,4 +1,5 @@
using AsyncEvent;
using FileTime.Core.Models; using FileTime.Core.Models;
namespace FileTime.Core.Providers namespace FileTime.Core.Providers
@@ -6,12 +7,9 @@ namespace FileTime.Core.Providers
public class TopContainer : IContainer public class TopContainer : IContainer
{ {
private readonly List<IContentProvider> _contentProviders; private readonly List<IContentProvider> _contentProviders;
private readonly IReadOnlyList<IContainer>? _containers;
public IReadOnlyList<IItem> Items => Containers; private readonly IReadOnlyList<IItem>? _items;
private readonly IReadOnlyList<IElement>? _elements = new List<IElement>().AsReadOnly();
public IReadOnlyList<IContainer> Containers { get; }
public IReadOnlyList<IElement> Elements { get; } = new List<IElement>().AsReadOnly();
public string Name => null; public string Name => null;
@@ -21,12 +19,13 @@ namespace FileTime.Core.Providers
public IContentProvider Provider => null; public IContentProvider Provider => null;
public event EventHandler? Refreshed; public AsyncEventHandler Refreshed { get; } = new();
public TopContainer(IEnumerable<IContentProvider> contentProviders) public TopContainer(IEnumerable<IContentProvider> contentProviders)
{ {
_contentProviders = new List<IContentProvider>(contentProviders); _contentProviders = new List<IContentProvider>(contentProviders);
Containers = _contentProviders.AsReadOnly(); _containers = _contentProviders.AsReadOnly();
_items = _containers.Cast<IItem>().ToList().AsReadOnly();
foreach (var contentProvider in contentProviders) foreach (var contentProvider in contentProviders)
{ {
@@ -34,21 +33,34 @@ namespace FileTime.Core.Providers
} }
} }
public IContainer CreateContainer(string name) => throw new NotImplementedException(); public Task<IContainer> CreateContainer(string name) => throw new NotImplementedException();
public IElement CreateElement(string name) => throw new NotImplementedException(); public Task<IElement> CreateElement(string name) => throw new NotImplementedException();
public void Delete() => throw new NotImplementedException(); public Task Delete() => throw new NotImplementedException();
public IItem? GetByPath(string path) => throw new NotImplementedException(); public Task<IItem?> GetByPath(string path) => throw new NotImplementedException();
public IContainer? GetParent() => null; public IContainer? GetParent() => null;
public bool IsExists(string name) => throw new NotImplementedException(); public Task<bool> IsExists(string name) => throw new NotImplementedException();
public void Refresh() public Task Refresh()
{ {
return Task.CompletedTask;
}
public Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
{
return Task.FromResult(_items);
}
public Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
{
return Task.FromResult(_containers);
}
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default)
{
return Task.FromResult(_elements);
} }
} }
} }

View File

@@ -45,6 +45,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTime.Uno.Windows.Deskto
EndProject EndProject
Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "FileTime.Uno.Windows.Package", "GuiApp\FileTime.Uno\FileTime.Uno.Windows.Package\FileTime.Uno.Windows.Package.wapproj", "{6BB770CB-B0C1-4A82-86B1-3C56F4393BE1}" Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "FileTime.Uno.Windows.Package", "GuiApp\FileTime.Uno\FileTime.Uno.Windows.Package\FileTime.Uno.Windows.Package.wapproj", "{6BB770CB-B0C1-4A82-86B1-3C56F4393BE1}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncEvent", "Core\AsyncEvent\AsyncEvent.csproj", "{9BDAC126-200F-4056-8D35-36EC059B40F3}"
EndProject
Global Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution GlobalSection(SharedMSBuildProjectFiles) = preSolution
GuiApp\FileTime.Uno\FileTime.Uno.Shared\FileTime.Uno.Shared.projitems*{02acef48-3be8-43e5-9358-a914bcbea7ca}*SharedItemsImports = 5 GuiApp\FileTime.Uno\FileTime.Uno.Shared\FileTime.Uno.Shared.projitems*{02acef48-3be8-43e5-9358-a914bcbea7ca}*SharedItemsImports = 5
@@ -356,6 +358,26 @@ Global
{6BB770CB-B0C1-4A82-86B1-3C56F4393BE1}.Release|x86.ActiveCfg = Release|x86 {6BB770CB-B0C1-4A82-86B1-3C56F4393BE1}.Release|x86.ActiveCfg = Release|x86
{6BB770CB-B0C1-4A82-86B1-3C56F4393BE1}.Release|x86.Build.0 = Release|x86 {6BB770CB-B0C1-4A82-86B1-3C56F4393BE1}.Release|x86.Build.0 = Release|x86
{6BB770CB-B0C1-4A82-86B1-3C56F4393BE1}.Release|x86.Deploy.0 = Release|x86 {6BB770CB-B0C1-4A82-86B1-3C56F4393BE1}.Release|x86.Deploy.0 = Release|x86
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Debug|ARM.ActiveCfg = Debug|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Debug|ARM.Build.0 = Debug|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Debug|ARM64.Build.0 = Debug|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Debug|x64.ActiveCfg = Debug|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Debug|x64.Build.0 = Debug|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Debug|x86.ActiveCfg = Debug|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Debug|x86.Build.0 = Debug|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Release|Any CPU.Build.0 = Release|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Release|ARM.ActiveCfg = Release|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Release|ARM.Build.0 = Release|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Release|ARM64.ActiveCfg = Release|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Release|ARM64.Build.0 = Release|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Release|x64.ActiveCfg = Release|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Release|x64.Build.0 = Release|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Release|x86.ActiveCfg = Release|Any CPU
{9BDAC126-200F-4056-8D35-36EC059B40F3}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -377,6 +399,7 @@ Global
{A7E1383B-E334-4CC2-AF34-D413213847B6} = {7CF1A80E-709A-4CB0-8434-7B2F0A4BC497} {A7E1383B-E334-4CC2-AF34-D413213847B6} = {7CF1A80E-709A-4CB0-8434-7B2F0A4BC497}
{02ACEF48-3BE8-43E5-9358-A914BCBEA7CA} = {7CF1A80E-709A-4CB0-8434-7B2F0A4BC497} {02ACEF48-3BE8-43E5-9358-A914BCBEA7CA} = {7CF1A80E-709A-4CB0-8434-7B2F0A4BC497}
{6BB770CB-B0C1-4A82-86B1-3C56F4393BE1} = {7CF1A80E-709A-4CB0-8434-7B2F0A4BC497} {6BB770CB-B0C1-4A82-86B1-3C56F4393BE1} = {7CF1A80E-709A-4CB0-8434-7B2F0A4BC497}
{9BDAC126-200F-4056-8D35-36EC059B40F3} = {38B1B927-4201-4B7A-87EE-737B8C6D4090}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8D679DCE-AC84-4A91-BFED-8F8D8E1D8183} SolutionGuid = {8D679DCE-AC84-4A91-BFED-8F8D8E1D8183}

View File

@@ -1,4 +1,5 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using AsyncEvent;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Providers; using FileTime.Core.Providers;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -8,15 +9,19 @@ namespace FileTime.Providers.Local
public class LocalContentProvider : IContentProvider public class LocalContentProvider : IContentProvider
{ {
private readonly ILogger<LocalContentProvider> _logger; private readonly ILogger<LocalContentProvider> _logger;
private IContainer _parent = null; private IContainer? _parent;
public IReadOnlyList<IContainer> RootContainers { get; } private readonly IReadOnlyList<IContainer> _rootContainers;
private readonly IReadOnlyList<IItem>? _items;
private readonly IReadOnlyList<IElement>? _elements = new List<IElement>().AsReadOnly();
/* public IReadOnlyList<IContainer> RootContainers { get; }
public IReadOnlyList<IItem> Items => RootContainers; public IReadOnlyList<IItem> Items => RootContainers;
public IReadOnlyList<IContainer> Containers => RootContainers; public IReadOnlyList<IContainer> Containers => RootContainers;
public IReadOnlyList<IElement> Elements { get; } = new List<IElement>(); public IReadOnlyList<IElement> Elements { get; } = new List<IElement>(); */
public string Name { get; } = "local"; public string Name { get; } = "local";
@@ -25,7 +30,7 @@ namespace FileTime.Providers.Local
public IContentProvider Provider => this; public IContentProvider Provider => this;
public event EventHandler? Refreshed; public AsyncEventHandler Refreshed { get; } = new();
public bool IsCaseInsensitive { get; } public bool IsCaseInsensitive { get; }
@@ -41,13 +46,14 @@ namespace FileTime.Providers.Local
FullName = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "" : null; FullName = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "" : null;
RootContainers = rootDirectories.Select(d => new LocalFolder(d, this, this)).OrderBy(d => d.Name).ToList().AsReadOnly(); _rootContainers = rootDirectories.Select(d => new LocalFolder(d, this, this)).OrderBy(d => d.Name).ToList().AsReadOnly();
_items = _rootContainers.Cast<IItem>().ToList().AsReadOnly();
} }
public IItem? GetByPath(string path) public async Task<IItem?> GetByPath(string path)
{ {
var pathParts = (IsCaseInsensitive ? path.ToLower() : path).TrimStart(Constants.SeparatorChar).Split(Constants.SeparatorChar); var pathParts = (IsCaseInsensitive ? path.ToLower() : path).TrimStart(Constants.SeparatorChar).Split(Constants.SeparatorChar);
var rootContainer = RootContainers.FirstOrDefault(c => NormalizePath(c.Name) == NormalizePath(pathParts[0])); var rootContainer = _rootContainers.FirstOrDefault(c => NormalizePath(c.Name) == NormalizePath(pathParts[0]));
if (rootContainer == null) if (rootContainer == null)
{ {
@@ -55,27 +61,43 @@ namespace FileTime.Providers.Local
return null; return null;
} }
return rootContainer.GetByPath(string.Join(Constants.SeparatorChar, pathParts.Skip(1))); return await rootContainer.GetByPath(string.Join(Constants.SeparatorChar, pathParts.Skip(1)));
} }
public void Refresh() public Task Refresh() => Task.CompletedTask;
{
}
public IContainer? GetParent() => _parent; public IContainer? GetParent() => _parent;
public IContainer CreateContainer(string name) => throw new NotSupportedException(); public Task<IContainer> CreateContainer(string name) => throw new NotSupportedException();
public IElement CreateElement(string name) => throw new NotSupportedException(); public Task<IElement> CreateElement(string name) => throw new NotSupportedException();
public bool IsExists(string name) => Items.Any(i => i.Name == name); public Task<bool> IsExists(string name) => Task.FromResult(_rootContainers.Any(i => i.Name == name));
public void Delete() => throw new NotSupportedException(); public Task Delete() => throw new NotSupportedException();
internal string NormalizePath(string path) => IsCaseInsensitive ? path.ToLower() : path; internal string NormalizePath(string path) => IsCaseInsensitive ? path.ToLower() : path;
public bool CanHandlePath(string path) => RootContainers.Any(r => path.StartsWith(r.Name)); public bool CanHandlePath(string path) => _rootContainers.Any(r => path.StartsWith(r.Name));
public void SetParent(IContainer container) public void SetParent(IContainer container)
{ {
_parent = container; _parent = container;
} }
public Task<IReadOnlyList<IContainer>?> GetRootContainers(CancellationToken token = default)
{
return Task.FromResult(_rootContainers);
}
public Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
{
return Task.FromResult(_items);
}
public Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
{
return Task.FromResult(_rootContainers);
}
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default)
{
return Task.FromResult(_elements);
}
} }
} }

View File

@@ -34,9 +34,10 @@ namespace FileTime.Providers.Local
public string GetPrimaryAttributeText() => _file.Length.ToSizeString(); public string GetPrimaryAttributeText() => _file.Length.ToSizeString();
public void Delete() public Task Delete()
{ {
_file.Delete(); _file.Delete();
return Task.CompletedTask;
} }
} }
} }

View File

@@ -1,3 +1,4 @@
using AsyncEvent;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Providers; using FileTime.Core.Providers;
@@ -15,44 +16,11 @@ namespace FileTime.Providers.Local
public LocalContentProvider Provider { get; } public LocalContentProvider Provider { get; }
IContentProvider IItem.Provider => Provider; IContentProvider IItem.Provider => Provider;
public IReadOnlyList<IItem> Items
{
get
{
if (_items == null) Refresh();
return _items!;
}
private set => _items = value;
}
public IReadOnlyList<IContainer> Containers
{
get
{
if (_containers == null) Refresh();
return _containers!;
}
private set => _containers = value;
}
public IReadOnlyList<IElement> Elements
{
get
{
if (_elements == null) Refresh();
return _elements!;
}
private set => _elements = value;
}
public string Name { get; } public string Name { get; }
public string FullName { get; } public string FullName { get; }
public event EventHandler? Refreshed; public AsyncEventHandler Refreshed { get; } = new();
public LocalFolder(DirectoryInfo directory, LocalContentProvider contentProvider, IContainer? parent) public LocalFolder(DirectoryInfo directory, LocalContentProvider contentProvider, IContainer? parent)
{ {
@@ -66,7 +34,7 @@ namespace FileTime.Providers.Local
public IContainer? GetParent() => _parent; public IContainer? GetParent() => _parent;
public void Refresh() public Task Refresh()
{ {
_containers = new List<IContainer>(); _containers = new List<IContainer>();
_elements = new List<IElement>(); _elements = new List<IElement>();
@@ -79,14 +47,32 @@ namespace FileTime.Providers.Local
catch { } catch { }
_items = _containers.Cast<IItem>().Concat(_elements).ToList().AsReadOnly(); _items = _containers.Cast<IItem>().Concat(_elements).ToList().AsReadOnly();
Refreshed?.Invoke(this, EventArgs.Empty); Refreshed?.InvokeAsync(this, AsyncEventArgs.Empty);
return Task.CompletedTask;
} }
public IItem? GetByPath(string path) public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
{
if (_items == null) await Refresh();
return _items;
}
public async Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
{
if (_containers == null) await Refresh();
return _containers;
}
public async Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default)
{
if (_elements == null) await Refresh();
return _elements;
}
public async Task<IItem?> GetByPath(string path)
{ {
var paths = path.Split(Constants.SeparatorChar); var paths = path.Split(Constants.SeparatorChar);
var item = Items.FirstOrDefault(i => Provider.NormalizePath(i.Name) == Provider.NormalizePath(paths[0])); var item = (await GetItems())!.FirstOrDefault(i => Provider.NormalizePath(i.Name) == Provider.NormalizePath(paths[0]));
if (paths.Length == 1) if (paths.Length == 1)
{ {
@@ -95,29 +81,33 @@ namespace FileTime.Providers.Local
if (item is IContainer container) if (item is IContainer container)
{ {
return container.GetByPath(string.Join(Constants.SeparatorChar, paths.Skip(1))); return await container.GetByPath(string.Join(Constants.SeparatorChar, paths.Skip(1)));
} }
return null; return null;
} }
public IContainer CreateContainer(string name) public async Task<IContainer> CreateContainer(string name)
{ {
Directory.CreateSubdirectory(name); Directory.CreateSubdirectory(name);
Refresh(); await Refresh();
return _containers!.FirstOrDefault(c => Provider.NormalizePath(c.Name) == Provider.NormalizePath(name))!; return _containers!.FirstOrDefault(c => Provider.NormalizePath(c.Name) == Provider.NormalizePath(name))!;
} }
public 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))) { }
Refresh(); await Refresh();
return _elements!.FirstOrDefault(e => Provider.NormalizePath(e.Name) == Provider.NormalizePath(name))!; return _elements!.FirstOrDefault(e => Provider.NormalizePath(e.Name) == Provider.NormalizePath(name))!;
} }
public bool IsExists(string name) => Items.Any(i => Provider.NormalizePath(i.Name) == Provider.NormalizePath(name)); public async Task<bool> IsExists(string name) => (await GetItems())?.Any(i => Provider.NormalizePath(i.Name) == Provider.NormalizePath(name)) ?? false;
public void Delete() => Directory.Delete(true); public Task Delete()
{
Directory.Delete(true);
return Task.CompletedTask;
}
} }
} }

View File

@@ -1,3 +1,4 @@
using AsyncEvent;
using FileTime.Core.Interactions; using FileTime.Core.Interactions;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Providers; using FileTime.Core.Providers;
@@ -7,16 +8,11 @@ namespace FileTime.Providers.Smb
public class SmbContentProvider : IContentProvider public class SmbContentProvider : IContentProvider
{ {
private IContainer _parent; private IContainer _parent;
private readonly List<IContainer> _rootContainers;
private readonly IInputInterface _inputInterface; private readonly IInputInterface _inputInterface;
private readonly List<IContainer> _rootContainers;
public IReadOnlyList<IContainer> RootContainers { get; } private readonly IReadOnlyList<IContainer> _rootContainersReadOnly;
private readonly IReadOnlyList<IItem>? _items;
public IReadOnlyList<IItem> Items => RootContainers; private readonly IReadOnlyList<IElement>? _elements = new List<IElement>().AsReadOnly();
public IReadOnlyList<IContainer> Containers => RootContainers;
public IReadOnlyList<IElement> Elements { get; } = new List<IElement>();
public string Name { get; } = "smb"; public string Name { get; } = "smb";
@@ -26,16 +22,16 @@ namespace FileTime.Providers.Smb
public IContentProvider Provider => this; public IContentProvider Provider => this;
public event EventHandler? Refreshed; public AsyncEventHandler Refreshed { get; } = new();
public SmbContentProvider(IInputInterface inputInterface) public SmbContentProvider(IInputInterface inputInterface)
{ {
_rootContainers = new List<IContainer>(); _rootContainers = new List<IContainer>();
RootContainers = _rootContainers.AsReadOnly(); _rootContainersReadOnly = _rootContainers.AsReadOnly();
_inputInterface = inputInterface; _inputInterface = inputInterface;
} }
public IContainer CreateContainer(string name) public async Task<IContainer> CreateContainer(string name)
{ {
var fullName = "\\\\" + name; var fullName = "\\\\" + name;
var container = _rootContainers.Find(c => c.Name == name); var container = _rootContainers.Find(c => c.Name == name);
@@ -46,37 +42,54 @@ namespace FileTime.Providers.Smb
_rootContainers.Add(container); _rootContainers.Add(container);
} }
Refresh(); await Refresh();
return container; return container;
} }
public IElement CreateElement(string name) public Task<IElement> CreateElement(string name)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
public void Delete() public Task Delete()
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
public IItem? GetByPath(string path) public Task<IItem?> GetByPath(string path)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public IContainer? GetParent() => _parent; public IContainer? GetParent() => _parent;
public bool IsExists(string name) => Items.Any(i => i.Name == name); public async Task<bool> IsExists(string name) => (await GetItems()).Any(i => i.Name == name);
public void Refresh() public async Task Refresh()
{ {
Refreshed?.Invoke(this, EventArgs.Empty); await Refreshed?.InvokeAsync(this, AsyncEventArgs.Empty);
} }
public bool CanHandlePath(string path) => path.StartsWith("smb://") || path.StartsWith(@"\\"); public bool CanHandlePath(string path) => path.StartsWith("smb://") || path.StartsWith(@"\\");
public void SetParent(IContainer container) => _parent = container; public void SetParent(IContainer container) => _parent = container;
public Task<IReadOnlyList<IContainer>?> GetRootContainers(CancellationToken token = default)
{
return Task.FromResult(_rootContainersReadOnly);
}
public Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
{
return Task.FromResult(_items);
}
public Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
{
return Task.FromResult(_rootContainersReadOnly);
}
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default)
{
return Task.FromResult(_elements);
}
} }
} }

View File

@@ -16,18 +16,15 @@ namespace FileTime.Providers.Smb
public IContentProvider Provider { get; } public IContentProvider Provider { get; }
private readonly Func<ISMBClient> _getSmbClient; public SmbFile(string name, SmbContentProvider provider, IContainer parent)
public SmbFile(string name, SmbContentProvider provider, IContainer parent, Func<ISMBClient> getSmbClient)
{ {
Name = name; Name = name;
FullName = parent.FullName + Constants.SeparatorChar + Name; FullName = parent.FullName + Constants.SeparatorChar + Name;
Provider = provider; Provider = provider;
_getSmbClient = getSmbClient;
} }
public void Delete() public Task Delete()
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@@ -1,3 +1,4 @@
using AsyncEvent;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Providers; using FileTime.Core.Providers;
using SMBLibrary; using SMBLibrary;
@@ -10,43 +11,9 @@ namespace FileTime.Providers.Smb
private IReadOnlyList<IItem>? _items; private IReadOnlyList<IItem>? _items;
private IReadOnlyList<IContainer>? _containers; private IReadOnlyList<IContainer>? _containers;
private IReadOnlyList<IElement>? _elements; private IReadOnlyList<IElement>? _elements;
private Func<ISMBClient> _getSmbClient;
private readonly SmbShare _smbShare; private readonly SmbShare _smbShare;
private readonly IContainer? _parent; private readonly IContainer? _parent;
public IReadOnlyList<IItem> Items
{
get
{
if (_items == null) Refresh();
return _items!;
}
private set => _items = value;
}
public IReadOnlyList<IContainer> Containers
{
get
{
if (_containers == null) Refresh();
return _containers!;
}
private set => _containers = value;
}
public IReadOnlyList<IElement> Elements
{
get
{
if (_elements == null) Refresh();
return _elements!;
}
private set => _elements = value;
}
public string Name { get; } public string Name { get; }
public string? FullName { get; } public string? FullName { get; }
@@ -56,39 +23,33 @@ namespace FileTime.Providers.Smb
public SmbContentProvider Provider { get; } public SmbContentProvider Provider { get; }
IContentProvider IItem.Provider => Provider; IContentProvider IItem.Provider => Provider;
public event EventHandler? Refreshed; public AsyncEventHandler Refreshed { get; } = new();
public SmbFolder(string name, SmbContentProvider contentProvider, SmbShare smbShare, IContainer parent, Func<ISMBClient> getSmbClient) public SmbFolder(string name, SmbContentProvider contentProvider, SmbShare smbShare, IContainer parent)
{ {
_parent = parent; _parent = parent;
_getSmbClient = getSmbClient; _smbShare = smbShare;
Name = name; Name = name;
FullName = parent?.FullName == null ? Name : parent.FullName + Constants.SeparatorChar + Name; FullName = parent?.FullName == null ? Name : parent.FullName + Constants.SeparatorChar + Name;
Provider = contentProvider; Provider = contentProvider;
_smbShare = smbShare;
} }
public IContainer CreateContainer(string name) public Task<IContainer> CreateContainer(string name)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public IElement CreateElement(string name) public Task<IElement> CreateElement(string name)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void Delete() public async Task<IItem?> GetByPath(string path)
{
throw new NotImplementedException();
}
public IItem? GetByPath(string path)
{ {
var paths = path.Split(Constants.SeparatorChar); var paths = path.Split(Constants.SeparatorChar);
var item = Items.FirstOrDefault(i => i.Name == paths[0]); var item = (await GetItems())?.FirstOrDefault(i => i.Name == paths[0]);
if (paths.Length == 1) if (paths.Length == 1)
{ {
@@ -97,7 +58,7 @@ namespace FileTime.Providers.Smb
if (item is IContainer container) if (item is IContainer container)
{ {
return container.GetByPath(string.Join(Constants.SeparatorChar, paths.Skip(1))); return await container.GetByPath(string.Join(Constants.SeparatorChar, paths.Skip(1)));
} }
return null; return null;
@@ -105,12 +66,17 @@ namespace FileTime.Providers.Smb
public IContainer? GetParent() => _parent; public IContainer? GetParent() => _parent;
public bool IsExists(string name) public Task<bool> IsExists(string name)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void Refresh() public Task Delete()
{
throw new NotImplementedException();
}
public async Task Refresh()
{ {
var containers = new List<IContainer>(); var containers = new List<IContainer>();
var elements = new List<IElement>(); var elements = new List<IElement>();
@@ -118,7 +84,7 @@ namespace FileTime.Providers.Smb
try try
{ {
var path = FullName![(_smbShare.FullName!.Length + 1)..]; var path = FullName![(_smbShare.FullName!.Length + 1)..];
(containers, elements) = _smbShare.ListFolder(this, _smbShare.Name, path); (containers, elements) = await _smbShare.ListFolder(this, _smbShare.Name, path);
} }
catch { } catch { }
@@ -126,7 +92,23 @@ 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();
Refreshed?.Invoke(this, EventArgs.Empty); await Refreshed?.InvokeAsync(this, AsyncEventArgs.Empty);
}
public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
{
if (_items == null) await Refresh();
return _items;
}
public async Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
{
if (_containers == null) await Refresh();
return _containers;
}
public async Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default)
{
if (_elements == null) await Refresh();
return _elements;
} }
} }
} }

View File

@@ -1,4 +1,5 @@
using System.Net; using System.Net;
using AsyncEvent;
using FileTime.Core.Interactions; using FileTime.Core.Interactions;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Providers; using FileTime.Core.Providers;
@@ -13,31 +14,11 @@ namespace FileTime.Providers.Smb
private string? _password; private string? _password;
private IReadOnlyList<IContainer>? _shares; private IReadOnlyList<IContainer>? _shares;
private IReadOnlyList<IItem>? _items;
private readonly IReadOnlyList<IElement>? _elements = new List<IElement>().AsReadOnly();
private ISMBClient? _client; private ISMBClient? _client;
private readonly IInputInterface _inputInterface; private readonly IInputInterface _inputInterface;
public IReadOnlyList<IItem> Items
{
get
{
if (_shares == null) Refresh();
return _shares!;
}
}
public IReadOnlyList<IContainer> Containers
{
get
{
if (_shares == null) Refresh();
return _shares!;
}
private set => _shares = value;
}
public IReadOnlyList<IElement> Elements { get; } = new List<IElement>().AsReadOnly();
public string Name { get; } public string Name { get; }
public string? FullName { get; } public string? FullName { get; }
@@ -48,7 +29,7 @@ namespace FileTime.Providers.Smb
IContentProvider IItem.Provider => Provider; IContentProvider IItem.Provider => Provider;
public event EventHandler? Refreshed; public AsyncEventHandler Refreshed { get; } = new();
public SmbServer(string path, SmbContentProvider contentProvider, IInputInterface inputInterface) public SmbServer(string path, SmbContentProvider contentProvider, IInputInterface inputInterface)
{ {
@@ -58,43 +39,60 @@ namespace FileTime.Providers.Smb
FullName = Name = path; FullName = Name = path;
} }
public IContainer CreateContainer(string name) public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
{
if (_shares == null) await Refresh();
return _shares;
}
public async Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
{
if (_shares == null) await Refresh();
return _shares;
}
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default)
{
return Task.FromResult(_elements);
}
public Task<IContainer> CreateContainer(string name)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
public IElement CreateElement(string name) public Task<IElement> CreateElement(string name)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
public void Delete() public Task Delete()
{ {
return Task.CompletedTask;
} }
public IItem? GetByPath(string path) public Task<IItem?> GetByPath(string path)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public IContainer? GetParent() => Provider; public IContainer? GetParent() => Provider;
public bool IsExists(string name) public Task<bool> IsExists(string name)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void Refresh() public async Task Refresh()
{ {
ISMBClient client = GetSmbClient(); ISMBClient client = await GetSmbClient();
List<string> shares = client.ListShares(out var status); List<string> shares = client.ListShares(out var status);
_shares = shares.ConvertAll(s => new SmbShare(s, Provider, this, GetSmbClient)).AsReadOnly(); _shares = shares.ConvertAll(s => new SmbShare(s, Provider, this, GetSmbClient)).AsReadOnly();
Refreshed?.Invoke(this, EventArgs.Empty); _items = _shares.Cast<IItem>().ToList().AsReadOnly();
await Refreshed?.InvokeAsync(this, AsyncEventArgs.Empty);
} }
private ISMBClient GetSmbClient() private async Task<ISMBClient> GetSmbClient()
{ {
if (_client == null) if (_client == null)
{ {
@@ -108,7 +106,7 @@ namespace FileTime.Providers.Smb
{ {
if (_username == null && _password == null) if (_username == null && _password == null)
{ {
var inputs = _inputInterface.ReadInputs( var inputs = await _inputInterface.ReadInputs(
new InputElement[] new InputElement[]
{ {
new InputElement($"Username for '{Name}'", InputType.Text), new InputElement($"Username for '{Name}'", InputType.Text),

View File

@@ -1,3 +1,4 @@
using AsyncEvent;
using FileTime.Core.Interactions; using FileTime.Core.Interactions;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Providers; using FileTime.Core.Providers;
@@ -11,42 +12,9 @@ namespace FileTime.Providers.Smb
private IReadOnlyList<IItem>? _items; private IReadOnlyList<IItem>? _items;
private IReadOnlyList<IContainer>? _containers; private IReadOnlyList<IContainer>? _containers;
private IReadOnlyList<IElement>? _elements; private IReadOnlyList<IElement>? _elements;
private Func<ISMBClient> _getSmbClient; private Func<Task<ISMBClient>> _getSmbClient;
private readonly IContainer? _parent; private readonly IContainer? _parent;
public IReadOnlyList<IItem> Items
{
get
{
if (_items == null) Refresh();
return _items!;
}
private set => _items = value;
}
public IReadOnlyList<IContainer> Containers
{
get
{
if (_containers == null) Refresh();
return _containers!;
}
private set => _containers = value;
}
public IReadOnlyList<IElement> Elements
{
get
{
if (_elements == null) Refresh();
return _elements!;
}
private set => _elements = value;
}
public string Name { get; } public string Name { get; }
public string? FullName { get; } public string? FullName { get; }
@@ -56,9 +24,9 @@ namespace FileTime.Providers.Smb
public SmbContentProvider Provider { get; } public SmbContentProvider Provider { get; }
IContentProvider IItem.Provider => Provider; IContentProvider IItem.Provider => Provider;
public event EventHandler? Refreshed; public AsyncEventHandler Refreshed { get; } = new();
public SmbShare(string name, SmbContentProvider contentProvider, IContainer parent, Func<ISMBClient> getSmbClient) public SmbShare(string name, SmbContentProvider contentProvider, IContainer parent, Func<Task<ISMBClient>> getSmbClient)
{ {
_parent = parent; _parent = parent;
_getSmbClient = getSmbClient; _getSmbClient = getSmbClient;
@@ -68,26 +36,42 @@ namespace FileTime.Providers.Smb
Provider = contentProvider; Provider = contentProvider;
} }
public IContainer CreateContainer(string name) public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
{
if (_items == null) await Refresh();
return _items;
}
public async Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
{
if (_containers == null) await Refresh();
return _containers;
}
public async Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default)
{
if (_elements == null) await Refresh();
return _elements;
}
public Task<IContainer> CreateContainer(string name)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public IElement CreateElement(string name) public Task<IElement> CreateElement(string name)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void Delete() public Task Delete()
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public IItem? GetByPath(string path) public async Task<IItem?> GetByPath(string path)
{ {
var paths = path.Split(Constants.SeparatorChar); var paths = path.Split(Constants.SeparatorChar);
var item = Items.FirstOrDefault(i => i.Name == paths[0]); var item = (await GetItems())?.FirstOrDefault(i => i.Name == paths[0]);
if (paths.Length == 1) if (paths.Length == 1)
{ {
@@ -96,7 +80,7 @@ namespace FileTime.Providers.Smb
if (item is IContainer container) if (item is IContainer container)
{ {
return container.GetByPath(string.Join(Constants.SeparatorChar, paths.Skip(1))); return await container.GetByPath(string.Join(Constants.SeparatorChar, paths.Skip(1)));
} }
return null; return null;
@@ -104,19 +88,19 @@ namespace FileTime.Providers.Smb
public IContainer? GetParent() => _parent; public IContainer? GetParent() => _parent;
public bool IsExists(string name) public Task<bool> IsExists(string name)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void Refresh() public async Task Refresh()
{ {
var containers = new List<IContainer>(); var containers = new List<IContainer>();
var elements = new List<IElement>(); var elements = new List<IElement>();
try try
{ {
(containers, elements) = ListFolder(this, Name, string.Empty); (containers, elements) = await ListFolder(this, Name, string.Empty);
} }
catch { } catch { }
@@ -124,15 +108,15 @@ 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();
Refreshed?.Invoke(this, EventArgs.Empty); await Refreshed?.InvokeAsync(this, AsyncEventArgs.Empty);
} }
public (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)
{ {
var containers = new List<IContainer>(); var containers = new List<IContainer>();
var elements = new List<IElement>(); var elements = new List<IElement>();
var client = _getSmbClient(); var client = await _getSmbClient();
ISMBFileStore fileStore = client.TreeConnect(shareName, out var status); ISMBFileStore fileStore = client.TreeConnect(shareName, out var status);
if (status == NTStatus.STATUS_SUCCESS) if (status == NTStatus.STATUS_SUCCESS)
{ {
@@ -148,11 +132,11 @@ namespace FileTime.Providers.Smb
{ {
if ((fileDirectoryInformation.FileAttributes & SMBLibrary.FileAttributes.Directory) == SMBLibrary.FileAttributes.Directory) if ((fileDirectoryInformation.FileAttributes & SMBLibrary.FileAttributes.Directory) == SMBLibrary.FileAttributes.Directory)
{ {
containers.Add(new SmbFolder(fileDirectoryInformation.FileName, Provider, this, parent, _getSmbClient)); containers.Add(new SmbFolder(fileDirectoryInformation.FileName, Provider, this, parent));
} }
else else
{ {
elements.Add(new SmbFile(fileDirectoryInformation.FileName, Provider, parent, _getSmbClient)); elements.Add(new SmbFile(fileDirectoryInformation.FileName, Provider, parent));
} }
} }
} }