Async core refactor
This commit is contained in:
@@ -65,9 +65,13 @@ namespace FileTime.App.Core.Tab
|
||||
return false;
|
||||
}
|
||||
|
||||
public IReadOnlyList<TabItem> GetCurrentSelectedItems() =>
|
||||
SelectedItems.ContainsKey(Tab.CurrentLocation)
|
||||
? SelectedItems[Tab.CurrentLocation]
|
||||
public async Task<IReadOnlyList<TabItem>> GetCurrentSelectedItems()
|
||||
{
|
||||
var currentLocation = await Tab.GetCurrentLocation();
|
||||
|
||||
return SelectedItems.ContainsKey(currentLocation)
|
||||
? SelectedItems[currentLocation]
|
||||
: new List<TabItem>().AsReadOnly();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,9 +70,13 @@ namespace FileTime.ConsoleUI.App.UI.Color
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.SetCursorPosition(0, 0);
|
||||
Write(new string(' ', Console.WindowHeight * Console.WindowWidth));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,12 +8,12 @@ namespace FileTime.ConsoleUI.App
|
||||
{
|
||||
private void CloseTab()
|
||||
{
|
||||
var currentTabIndex = _panes.IndexOf(_selectedTab!);
|
||||
var currentTabIndex = _tabs.IndexOf(_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
|
||||
{
|
||||
@@ -22,34 +22,36 @@ namespace FileTime.ConsoleUI.App
|
||||
}
|
||||
}
|
||||
|
||||
private void MoveCursorUp() => _selectedTab!.SelectPreviousItem();
|
||||
private void MoveCursorDown() => _selectedTab!.SelectNextItem();
|
||||
private void GoUp() => _selectedTab!.GoUp();
|
||||
private void Open() => _selectedTab!.Open();
|
||||
private async Task MoveCursorUp() => await _selectedTab!.SelectPreviousItem();
|
||||
private async Task MoveCursorDown() => await _selectedTab!.SelectNextItem();
|
||||
private async Task GoUp() => await _selectedTab!.GoUp();
|
||||
private async Task Open() => await _selectedTab!.Open();
|
||||
|
||||
private void MoveCursorUpPage() => _selectedTab!.SelectPreviousItem(_renderers[_selectedTab].PageSize);
|
||||
private void MoveCursorDownPage() => _selectedTab!.SelectNextItem(_renderers[_selectedTab].PageSize);
|
||||
private void MoveCursorToTop() => _selectedTab!.SelectFirstItem();
|
||||
private void MoveCursorToBottom() => _selectedTab!.SelectLastItem();
|
||||
private async Task MoveCursorUpPage() => await _selectedTab!.SelectPreviousItem(_renderers[_selectedTab].PageSize);
|
||||
private async Task MoveCursorDownPage() => await _selectedTab!.SelectNextItem(_renderers[_selectedTab].PageSize);
|
||||
private async Task MoveCursorToTop() => await _selectedTab!.SelectFirstItem();
|
||||
private async Task MoveCursorToBottom() => await _selectedTab!.SelectLastItem();
|
||||
|
||||
private void ToggleHidden()
|
||||
private async Task ToggleHidden()
|
||||
{
|
||||
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)
|
||||
? oldVirtualContainer.ExceptWithName(hiddenFilterName)
|
||||
: GenerateHiddenFilterVirtualContainer(_selectedTab.CurrentLocation);
|
||||
: GenerateHiddenFilterVirtualContainer(currentLocation);
|
||||
}
|
||||
else
|
||||
{
|
||||
containerToOpen = GenerateHiddenFilterVirtualContainer(_selectedTab.CurrentLocation);
|
||||
containerToOpen = GenerateHiddenFilterVirtualContainer(currentLocation);
|
||||
}
|
||||
|
||||
_selectedTab.OpenContainer(containerToOpen);
|
||||
await _selectedTab.OpenContainer(containerToOpen);
|
||||
|
||||
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;
|
||||
if (_paneStates[_selectedTab].ContainsSelectedItem(currentSelectedItem.Provider, _selectedTab.CurrentLocation, currentSelectedItem.FullName!))
|
||||
var currentSelectedItem = await _selectedTab.GetCurrentSelectedItem()!;
|
||||
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
|
||||
{
|
||||
_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.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);
|
||||
}
|
||||
}
|
||||
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>();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -140,9 +145,10 @@ namespace FileTime.ConsoleUI.App
|
||||
command.Sources.Add(item);
|
||||
}
|
||||
|
||||
command.Target = _selectedTab.CurrentLocation is VirtualContainer virtualContainer
|
||||
var currentLocation = await _selectedTab!.GetCurrentLocation();
|
||||
command.Target = currentLocation is VirtualContainer virtualContainer
|
||||
? virtualContainer.BaseContainer
|
||||
: _selectedTab.CurrentLocation;
|
||||
: currentLocation;
|
||||
|
||||
_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();
|
||||
MoveToIOLine(2);
|
||||
_coloredConsoleRenderer.Write("New container name: ");
|
||||
var newContainerName = _consoleReader.ReadText(validator: Validator);
|
||||
var newContainerName = await _consoleReader.ReadText(validator: Validator);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
if (_paneStates[_selectedTab!].GetCurrentSelectedItems().Count == 1
|
||||
&& _paneStates[_selectedTab!].GetCurrentSelectedItems()[0] is IContainer container
|
||||
&& container.Items.Count > 0)
|
||||
//FIXME: check 'is Container'
|
||||
if (currentSelectedItems.Count == 1
|
||||
&& currentSelectedItems[0] is IContainer container
|
||||
&& (await container.GetItems())?.Count > 0)
|
||||
{
|
||||
delete = AskForApprove($"The container '{container.Name}' is not empty.");
|
||||
}
|
||||
|
||||
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;
|
||||
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.");
|
||||
}
|
||||
@@ -210,7 +220,7 @@ namespace FileTime.ConsoleUI.App
|
||||
{
|
||||
itemsToDelete = new List<IAbsolutePath>()
|
||||
{
|
||||
new AbsolutePath(_selectedTab.CurrentSelectedItem.Provider, _selectedTab.CurrentSelectedItem.FullName!)
|
||||
new AbsolutePath(currentSelectedItem.Provider, currentSelectedItem.FullName!)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace FileTime.ConsoleUI.App
|
||||
{
|
||||
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, TabState> _paneStates = new();
|
||||
private Tab? _selectedTab;
|
||||
@@ -48,29 +48,30 @@ namespace FileTime.ConsoleUI.App
|
||||
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);
|
||||
_panes.Add(pane);
|
||||
var tab = new Tab();
|
||||
await tab.Init(container);
|
||||
_tabs.Add(tab);
|
||||
|
||||
var paneState = new TabState(pane);
|
||||
_paneStates.Add(pane, paneState);
|
||||
var paneState = new TabState(tab);
|
||||
_paneStates.Add(tab, paneState);
|
||||
|
||||
var renderer = _serviceProvider.GetService<Render>()!;
|
||||
renderer.Init(pane, paneState);
|
||||
_renderers.Add(pane, renderer);
|
||||
renderer.Init(tab, paneState);
|
||||
_renderers.Add(tab, renderer);
|
||||
|
||||
return pane;
|
||||
return tab;
|
||||
}
|
||||
|
||||
private void RemoveTab(Tab pane)
|
||||
{
|
||||
_panes.Remove(pane);
|
||||
_tabs.Remove(pane);
|
||||
_renderers.Remove(pane);
|
||||
_paneStates.Remove(pane);
|
||||
}
|
||||
@@ -181,15 +182,15 @@ namespace FileTime.ConsoleUI.App
|
||||
_commandBindings.AddRange(commandBindings);
|
||||
}
|
||||
|
||||
public void PrintUI()
|
||||
public async Task PrintUI(CancellationToken token = default)
|
||||
{
|
||||
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;
|
||||
_previousKeys.Add(keyinfo);
|
||||
@@ -208,7 +209,7 @@ namespace FileTime.ConsoleUI.App
|
||||
}
|
||||
else if (selectedCommandBinding != null)
|
||||
{
|
||||
selectedCommandBinding.Invoke();
|
||||
await selectedCommandBinding.InvokeAsync();
|
||||
_previousKeys.Clear();
|
||||
}
|
||||
else
|
||||
|
||||
@@ -4,14 +4,14 @@ namespace FileTime.ConsoleUI.App.Command
|
||||
{
|
||||
public class CommandBinding
|
||||
{
|
||||
private readonly Action _commandHandler;
|
||||
private readonly Func<Task> _commandHandler;
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public ConsoleKeyInfo[] Keys { 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;
|
||||
Command = command;
|
||||
@@ -19,6 +19,11 @@ namespace FileTime.ConsoleUI.App.Command
|
||||
_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();
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace FileTime.ConsoleUI.App.UI
|
||||
_coloredConsoleRenderer = coloredConsoleRenderer;
|
||||
_consoleReader = consoleReader;
|
||||
}
|
||||
public string?[] ReadInputs(IEnumerable<InputElement> fields)
|
||||
public async Task<string?[]> ReadInputs(IEnumerable<InputElement> fields)
|
||||
{
|
||||
var results = new List<string?>();
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace FileTime.ConsoleUI.App.UI
|
||||
_application.MoveToIOLine();
|
||||
_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();
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace FileTime.ConsoleUI.App.UI
|
||||
{
|
||||
_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;
|
||||
try
|
||||
@@ -73,7 +73,7 @@ namespace FileTime.ConsoleUI.App.UI
|
||||
|
||||
position++;
|
||||
}
|
||||
validator?.Invoke(input);
|
||||
await validator?.Invoke(input);
|
||||
|
||||
Console.SetCursorPosition(currentConsoleLeft, currentConsoleTop);
|
||||
_coloredConsoleRenderer.Write($"{{0,-{maxLength}}}", placeHolder == null ? input : new string((char)placeHolder, input.Length));
|
||||
|
||||
@@ -48,33 +48,37 @@ namespace FileTime.ConsoleUI.App.UI
|
||||
TabState = paneState;
|
||||
}
|
||||
|
||||
public void PrintUI()
|
||||
public async Task PrintUI(CancellationToken token = default)
|
||||
{
|
||||
if (Tab != null)
|
||||
{
|
||||
PrintPrompt();
|
||||
PrintTabs();
|
||||
await PrintPrompt(token);
|
||||
await PrintTabs(token);
|
||||
}
|
||||
}
|
||||
|
||||
private void PrintTabs()
|
||||
private async Task PrintTabs(CancellationToken token = default)
|
||||
{
|
||||
var previousColumnWidth = (int)Math.Floor(Console.WindowWidth * 0.15) - 1;
|
||||
var currentColumnWidth = (int)Math.Floor(Console.WindowWidth * 0.4) - 1;
|
||||
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.CloneVirtualChainFor(parentContainer, v => v.IsTransitive)
|
||||
: parentContainer,
|
||||
currentVirtualContainer != null
|
||||
? currentVirtualContainer.GetRealContainer()
|
||||
: Tab.CurrentLocation,
|
||||
: currentLocation,
|
||||
PrintMode.Previous,
|
||||
0,
|
||||
_contentPaddingTop,
|
||||
@@ -90,29 +94,35 @@ namespace FileTime.ConsoleUI.App.UI
|
||||
_contentRowCount);
|
||||
}
|
||||
|
||||
Tab.CurrentLocation.Refresh();
|
||||
if (token.IsCancellationRequested) return;
|
||||
|
||||
CheckAndSetCurrentDisplayStartY();
|
||||
PrintColumn(
|
||||
Tab.CurrentLocation,
|
||||
Tab.CurrentSelectedItem,
|
||||
await currentLocation.Refresh();
|
||||
|
||||
await CheckAndSetCurrentDisplayStartY();
|
||||
await PrintColumn(
|
||||
currentLocation,
|
||||
currentSelectedItem,
|
||||
PrintMode.Current,
|
||||
previousColumnWidth + 1,
|
||||
_contentPaddingTop,
|
||||
currentColumnWidth,
|
||||
_contentRowCount);
|
||||
|
||||
if (Tab.CurrentSelectedItem is IContainer selectedContainer)
|
||||
if (token.IsCancellationRequested) return;
|
||||
|
||||
if (currentSelectedItem is IContainer selectedContainer)
|
||||
{
|
||||
selectedContainer.Refresh();
|
||||
await selectedContainer.Refresh();
|
||||
|
||||
selectedContainer = currentVirtualContainer != null
|
||||
? currentVirtualContainer.CloneVirtualChainFor(selectedContainer, v => v.IsTransitive)
|
||||
: selectedContainer;
|
||||
|
||||
PrintColumn(
|
||||
var selectedContainerItems = (await selectedContainer.GetItems())!;
|
||||
|
||||
await PrintColumn(
|
||||
selectedContainer,
|
||||
selectedContainer.Items.Count > 0 ? selectedContainer.Items[0] : null,
|
||||
selectedContainerItems.Count > 0 ? selectedContainerItems[0] : null,
|
||||
PrintMode.Next,
|
||||
previousColumnWidth + currentColumnWidth + 2,
|
||||
_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);
|
||||
_coloredRenderer.ResetColor();
|
||||
_coloredRenderer.ForegroundColor = _appStyle.AccentForeground;
|
||||
@@ -140,24 +153,24 @@ namespace FileTime.ConsoleUI.App.UI
|
||||
_coloredRenderer.Write(' ');
|
||||
|
||||
_coloredRenderer.ForegroundColor = _appStyle.ContainerForeground;
|
||||
var path = Tab!.CurrentLocation.FullName + "/";
|
||||
var path = currentLocation.FullName + "/";
|
||||
_coloredRenderer.Write(path);
|
||||
|
||||
if (Tab.CurrentSelectedItem?.Name != null)
|
||||
if (currentSelectedItem?.Name != null)
|
||||
{
|
||||
_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 currentY = 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
|
||||
{
|
||||
@@ -169,7 +182,7 @@ namespace FileTime.ConsoleUI.App.UI
|
||||
|
||||
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;
|
||||
foreach (var item in itemsToPrint)
|
||||
{
|
||||
@@ -182,7 +195,7 @@ namespace FileTime.ConsoleUI.App.UI
|
||||
var container = item as IContainer;
|
||||
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? foregroundColor = null;
|
||||
@@ -213,7 +226,7 @@ namespace FileTime.ConsoleUI.App.UI
|
||||
foregroundColor = _appStyle.SelectedItemForeground;
|
||||
}
|
||||
|
||||
if (item == currentItem)
|
||||
if (item.Name == currentItem?.Name)
|
||||
{
|
||||
(backgroundColor, foregroundColor) = (foregroundColor, backgroundColor);
|
||||
}
|
||||
@@ -224,6 +237,7 @@ namespace FileTime.ConsoleUI.App.UI
|
||||
var text = string.Format($"{{0,-{elementWidth}}}", _paddingLeft + (isSelected ? " " : "") + namePart + _paddingRight);
|
||||
text = string.Concat(text.AsSpan(0, text.Length - attributePart.Length - 1), " ", attributePart);
|
||||
|
||||
if (token.IsCancellationRequested) return;
|
||||
Console.SetCursorPosition(startX, startY + currentY++);
|
||||
_coloredRenderer.Write(text);
|
||||
|
||||
@@ -262,7 +276,7 @@ namespace FileTime.ConsoleUI.App.UI
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckAndSetCurrentDisplayStartY()
|
||||
private async Task CheckAndSetCurrentDisplayStartY()
|
||||
{
|
||||
const int padding = 5;
|
||||
|
||||
@@ -273,7 +287,7 @@ namespace FileTime.ConsoleUI.App.UI
|
||||
}
|
||||
|
||||
while (Tab.CurrentSelectedIndex > _currentDisplayStartY + _contentRowCount - padding
|
||||
&& _currentDisplayStartY < Tab.CurrentLocation.Items.Count - _contentRowCount)
|
||||
&& _currentDisplayStartY < (await (await Tab.GetCurrentLocation()).GetItems())!.Count - _contentRowCount)
|
||||
{
|
||||
_currentDisplayStartY++;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,49 +15,62 @@ namespace FileTime.ConsoleUI
|
||||
public class Program
|
||||
{
|
||||
static ILogger<Program>? _logger;
|
||||
static Thread? _renderThread;
|
||||
|
||||
public static void Main()
|
||||
public static async Task Main()
|
||||
{
|
||||
var serviceProvider = CreateServiceProvider();
|
||||
_logger = serviceProvider.GetService<ILogger<Program>>()!;
|
||||
|
||||
var coloredConsoleRenderer = serviceProvider.GetService<IColoredConsoleRenderer>()!;
|
||||
var localContentProvider = serviceProvider.GetService<LocalContentProvider>()!;
|
||||
var renderSynchronizer = serviceProvider.GetService<RenderSynchronizer>()!;
|
||||
|
||||
var currentPath = Environment.CurrentDirectory.Replace(Path.DirectorySeparatorChar, Constants.SeparatorChar);
|
||||
_logger.LogInformation("Current directory: '{0}'", currentPath);
|
||||
var currentPossibleDirectory = localContentProvider.GetByPath(currentPath);
|
||||
var currentPossibleDirectory = await localContentProvider.GetByPath(currentPath);
|
||||
|
||||
if (currentPossibleDirectory is IContainer container)
|
||||
{
|
||||
serviceProvider.GetService<TopContainer>();
|
||||
coloredConsoleRenderer.Clear();
|
||||
try
|
||||
{
|
||||
Console.CursorVisible = false;
|
||||
}
|
||||
catch { }
|
||||
|
||||
var app = serviceProvider.GetService<Application>()!;
|
||||
app.SetContainer(container);
|
||||
app.PrintUI();
|
||||
await app.SetContainer(container);
|
||||
renderSynchronizer.NeedsReRender();
|
||||
|
||||
_renderThread = new Thread(new ThreadStart(renderSynchronizer.Start));
|
||||
_renderThread.Start();
|
||||
|
||||
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.CursorVisible = true;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsAnsiColorSupported()
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.CursorLeft = 0;
|
||||
Console.CursorTop = 0;
|
||||
@@ -66,12 +79,19 @@ namespace FileTime.ConsoleUI
|
||||
|
||||
return Console.CursorLeft == 1 && Console.CursorTop == 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static ServiceProvider CreateServiceProvider()
|
||||
{
|
||||
return DependencyInjection.RegisterDefaultServices()
|
||||
.AddLogging(/* (builder) => builder.AddConsole().AddDebug() */)
|
||||
//.AddLogging()
|
||||
.AddLogging((builder) => builder.AddConsole().AddDebug())
|
||||
.AddSingleton<Application>()
|
||||
.AddSingleton<RenderSynchronizer>()
|
||||
.AddSingleton<IStyles>(new Styles(IsAnsiColorSupported()))
|
||||
.AddSingleton<IColoredConsoleRenderer, ColoredConsoleRenderer>()
|
||||
.AddSingleton<ConsoleReader>()
|
||||
|
||||
9
src/Core/AsyncEvent/AsyncEvent.csproj
Normal file
9
src/Core/AsyncEvent/AsyncEvent.csproj
Normal file
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
7
src/Core/AsyncEvent/AsyncEventArgs.cs
Normal file
7
src/Core/AsyncEvent/AsyncEventArgs.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace AsyncEvent
|
||||
{
|
||||
public class AsyncEventArgs
|
||||
{
|
||||
public static AsyncEventArgs Empty = new AsyncEventArgs();
|
||||
}
|
||||
}
|
||||
68
src/Core/AsyncEvent/AsyncEventHandler.cs
Normal file
68
src/Core/AsyncEvent/AsyncEventHandler.cs
Normal 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) { }
|
||||
}
|
||||
}
|
||||
@@ -16,38 +16,39 @@ namespace FileTime.Core.Command
|
||||
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)
|
||||
{
|
||||
var item = source.ContentProvider.GetByPath(source.Path);
|
||||
var item = await source.ContentProvider.GetByPath(source.Path);
|
||||
|
||||
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 childFiles = container.Elements.Select(f => new AbsolutePath(item.Provider, f.FullName!));
|
||||
var childDirectories = (await container.GetContainers())!.Select(d => new AbsolutePath(item.Provider, d.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)
|
||||
{
|
||||
var targetName = element.Name;
|
||||
|
||||
var targetNameExists = await target.IsExists(targetName);
|
||||
if (transportMode == TransportMode.Merge)
|
||||
{
|
||||
for (var i = 0; target.IsExists(targetName); i++)
|
||||
for (var i = 0; targetNameExists; i++)
|
||||
{
|
||||
targetName = element.Name + (i == 0 ? "_" : $"_{i}");
|
||||
}
|
||||
}
|
||||
else if (transportMode == TransportMode.Skip && target.IsExists(targetName))
|
||||
else if (transportMode == TransportMode.Skip && targetNameExists)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -12,29 +12,29 @@ namespace FileTime.Core.Command
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Execute()
|
||||
public async Task Execute()
|
||||
{
|
||||
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)
|
||||
{
|
||||
foreach (var child in container.Items)
|
||||
foreach (var child in await container.GetItems())
|
||||
{
|
||||
DoDelete(child);
|
||||
child.Delete();
|
||||
await DoDelete(child);
|
||||
await child.Delete();
|
||||
}
|
||||
|
||||
item.Delete();
|
||||
await item.Delete();
|
||||
}
|
||||
else if(item is IElement element)
|
||||
{
|
||||
element.Delete();
|
||||
await element.Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@ namespace FileTime.Core.Command
|
||||
{
|
||||
public interface IExecutableCommand : ICommand
|
||||
{
|
||||
void Execute();
|
||||
Task Execute();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.Core.Components
|
||||
@@ -7,7 +8,7 @@ namespace FileTime.Core.Components
|
||||
private IItem? _currentSelectedItem;
|
||||
private IContainer _currentLocation;
|
||||
|
||||
public IContainer CurrentLocation
|
||||
/* public IContainer CurrentLocation
|
||||
{
|
||||
get => _currentLocation;
|
||||
private set
|
||||
@@ -38,75 +39,122 @@ namespace FileTime.Core.Components
|
||||
CurrentSelectedItemChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
} */
|
||||
public int CurrentSelectedIndex { get; private set; }
|
||||
|
||||
public event EventHandler CurrentLocationChanged;
|
||||
public event EventHandler CurrentSelectedItemChanged;
|
||||
|
||||
public Tab(IContainer currentPath)
|
||||
public async Task Init(IContainer currentPath)
|
||||
{
|
||||
CurrentLocation = currentPath;
|
||||
CurrentSelectedItem = CurrentLocation.Items.Count > 0 ? CurrentLocation.Items[0] : null;
|
||||
await SetCurrentLocation(currentPath);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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();
|
||||
SelectItem(possibleItemsToSelect);
|
||||
if (possibleItemsToSelect.Count == 0) possibleItemsToSelect = currentLocationItems.ToList();
|
||||
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();
|
||||
SelectItem(possibleItemsToSelect);
|
||||
if (possibleItemsToSelect.Count == 0) possibleItemsToSelect = currentLocationItems.Reverse().ToList();
|
||||
await SelectItem(possibleItemsToSelect);
|
||||
}
|
||||
|
||||
private void SelectItem(IEnumerable<IItem> currentPossibleItems)
|
||||
private async Task SelectItem(IEnumerable<IItem> currentPossibleItems)
|
||||
{
|
||||
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;
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
root = root.GetParent();
|
||||
}
|
||||
|
||||
CurrentLocation = root;
|
||||
await SetCurrentLocation(root);
|
||||
}
|
||||
|
||||
public void GoUp()
|
||||
public async Task GoUp()
|
||||
{
|
||||
var lastCurrentLocation = CurrentLocation;
|
||||
var parent = CurrentLocation.GetParent();
|
||||
var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
|
||||
var lastCurrentLocation = await GetCurrentLocation();
|
||||
var parent = (await GetCurrentLocation()).GetParent();
|
||||
|
||||
if (parent is not null)
|
||||
{
|
||||
if (lastCurrentLocation is VirtualContainer lastCurrentVirtualContainer)
|
||||
{
|
||||
CurrentLocation = lastCurrentVirtualContainer.CloneVirtualChainFor(parent, v => v.IsPermanent);
|
||||
CurrentSelectedItem = lastCurrentVirtualContainer.GetRealContainer();
|
||||
await SetCurrentLocation(lastCurrentVirtualContainer.CloneVirtualChainFor(parent, v => v.IsPermanent));
|
||||
await SetCurrentSelectedItem(lastCurrentVirtualContainer.GetRealContainer());
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentLocation = parent;
|
||||
CurrentSelectedItem = lastCurrentLocation;
|
||||
await SetCurrentLocation(parent);
|
||||
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 (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
|
||||
{
|
||||
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;
|
||||
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;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\AppCommon\FileTime.App.Style\FileTime.App.Style.csproj" />
|
||||
<ProjectReference Include="..\AsyncEvent\AsyncEvent.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -2,6 +2,6 @@ namespace FileTime.Core.Interactions
|
||||
{
|
||||
public interface IInputInterface
|
||||
{
|
||||
string?[] ReadInputs(IEnumerable<InputElement> fields);
|
||||
Task<string?[]> ReadInputs(IEnumerable<InputElement> fields);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,21 @@
|
||||
using AsyncEvent;
|
||||
|
||||
namespace FileTime.Core.Models
|
||||
{
|
||||
public interface IContainer : IItem
|
||||
{
|
||||
IReadOnlyList<IItem> Items { get; }
|
||||
IReadOnlyList<IContainer> Containers { get; }
|
||||
IReadOnlyList<IElement> Elements { get; }
|
||||
Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default);
|
||||
Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default);
|
||||
Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default);
|
||||
|
||||
void Refresh();
|
||||
Task Refresh();
|
||||
IContainer? GetParent();
|
||||
IItem? GetByPath(string path);
|
||||
IContainer CreateContainer(string name);
|
||||
IElement CreateElement(string name);
|
||||
Task<IItem?> GetByPath(string path);
|
||||
Task<IContainer> CreateContainer(string name);
|
||||
Task<IElement> CreateElement(string name);
|
||||
|
||||
bool IsExists(string name);
|
||||
Task<bool> IsExists(string name);
|
||||
|
||||
event EventHandler? Refreshed;
|
||||
AsyncEventHandler Refreshed { get; }
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,6 @@ namespace FileTime.Core.Models
|
||||
string? FullName { get; }
|
||||
bool IsHidden { get; }
|
||||
IContentProvider Provider { get; }
|
||||
void Delete();
|
||||
Task Delete();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Providers;
|
||||
|
||||
namespace FileTime.Core.Models
|
||||
@@ -12,11 +13,11 @@ namespace FileTime.Core.Models
|
||||
public bool IsPermanent { get; }
|
||||
public bool IsTransitive { 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;
|
||||
|
||||
@@ -26,10 +27,15 @@ namespace FileTime.Core.Models
|
||||
|
||||
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;
|
||||
remove => BaseContainer.Refreshed -= value;
|
||||
BaseContainer.Refreshed.Add(handler);
|
||||
}
|
||||
private void RefreshRemoveBase(Func<object?, AsyncEventArgs, Task> handler)
|
||||
{
|
||||
BaseContainer.Refreshed.Add(handler);
|
||||
}
|
||||
|
||||
public VirtualContainer(
|
||||
@@ -40,32 +46,40 @@ namespace FileTime.Core.Models
|
||||
bool isTransitive = false,
|
||||
string? virtualContainerName = null)
|
||||
{
|
||||
Refreshed = new (RefreshAddBase, RefreshRemoveBase);
|
||||
BaseContainer = baseContainer;
|
||||
_containerTransformators = containerTransformators;
|
||||
_elementTransformators = elementTransformators;
|
||||
|
||||
InitItems();
|
||||
IsPermanent = isPermanent;
|
||||
IsTransitive = isTransitive;
|
||||
VirtualContainerName = virtualContainerName;
|
||||
}
|
||||
|
||||
private void InitItems()
|
||||
public async Task Init()
|
||||
{
|
||||
Containers = _containerTransformators.Aggregate(BaseContainer.Containers.AsEnumerable(), (a, t) => t(a)).ToList().AsReadOnly();
|
||||
Elements = _elementTransformators.Aggregate(BaseContainer.Elements.AsEnumerable(), (a, t) => t(a)).ToList().AsReadOnly();
|
||||
|
||||
Items = Containers.Cast<IItem>().Concat(Elements).ToList().AsReadOnly();
|
||||
await InitItems();
|
||||
}
|
||||
|
||||
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 void Refresh()
|
||||
public async Task Refresh()
|
||||
{
|
||||
BaseContainer.Refresh();
|
||||
InitItems();
|
||||
await BaseContainer.Refresh();
|
||||
await InitItems();
|
||||
}
|
||||
|
||||
public IContainer GetRealContainer() =>
|
||||
@@ -113,10 +127,23 @@ namespace FileTime.Core.Models
|
||||
: baseContainer;
|
||||
}
|
||||
|
||||
public IContainer CreateContainer(string name) => BaseContainer.CreateContainer(name);
|
||||
public IElement CreateElement(string name) => BaseContainer.CreateElement(name);
|
||||
public bool IsExists(string name) => BaseContainer.IsExists(name);
|
||||
public async Task<IContainer> CreateContainer(string name) => await BaseContainer.CreateContainer(name);
|
||||
public async Task<IElement> CreateElement(string name) => await BaseContainer.CreateElement(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();
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ namespace FileTime.Core.Providers
|
||||
{
|
||||
public interface IContentProvider : IContainer
|
||||
{
|
||||
IReadOnlyList<IContainer> RootContainers { get; }
|
||||
Task<IReadOnlyList<IContainer>> GetRootContainers(CancellationToken token = default);
|
||||
|
||||
bool CanHandlePath(string path);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.Core.Providers
|
||||
@@ -6,12 +7,9 @@ namespace FileTime.Core.Providers
|
||||
public class TopContainer : IContainer
|
||||
{
|
||||
private readonly List<IContentProvider> _contentProviders;
|
||||
|
||||
public IReadOnlyList<IItem> Items => Containers;
|
||||
|
||||
public IReadOnlyList<IContainer> Containers { get; }
|
||||
|
||||
public IReadOnlyList<IElement> Elements { get; } = new List<IElement>().AsReadOnly();
|
||||
private readonly IReadOnlyList<IContainer>? _containers;
|
||||
private readonly IReadOnlyList<IItem>? _items;
|
||||
private readonly IReadOnlyList<IElement>? _elements = new List<IElement>().AsReadOnly();
|
||||
|
||||
public string Name => null;
|
||||
|
||||
@@ -21,12 +19,13 @@ namespace FileTime.Core.Providers
|
||||
|
||||
public IContentProvider Provider => null;
|
||||
|
||||
public event EventHandler? Refreshed;
|
||||
public AsyncEventHandler Refreshed { get; } = new();
|
||||
|
||||
public TopContainer(IEnumerable<IContentProvider> contentProviders)
|
||||
{
|
||||
_contentProviders = new List<IContentProvider>(contentProviders);
|
||||
Containers = _contentProviders.AsReadOnly();
|
||||
_containers = _contentProviders.AsReadOnly();
|
||||
_items = _containers.Cast<IItem>().ToList().AsReadOnly();
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileTime.Uno.Windows.Deskto
|
||||
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}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncEvent", "Core\AsyncEvent\AsyncEvent.csproj", "{9BDAC126-200F-4056-8D35-36EC059B40F3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||
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.Build.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
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -377,6 +399,7 @@ Global
|
||||
{A7E1383B-E334-4CC2-AF34-D413213847B6} = {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}
|
||||
{9BDAC126-200F-4056-8D35-36EC059B40F3} = {38B1B927-4201-4B7A-87EE-737B8C6D4090}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {8D679DCE-AC84-4A91-BFED-8F8D8E1D8183}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Providers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -8,15 +9,19 @@ namespace FileTime.Providers.Local
|
||||
public class LocalContentProvider : IContentProvider
|
||||
{
|
||||
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<IContainer> Containers => RootContainers;
|
||||
|
||||
public IReadOnlyList<IElement> Elements { get; } = new List<IElement>();
|
||||
public IReadOnlyList<IElement> Elements { get; } = new List<IElement>(); */
|
||||
|
||||
public string Name { get; } = "local";
|
||||
|
||||
@@ -25,7 +30,7 @@ namespace FileTime.Providers.Local
|
||||
|
||||
public IContentProvider Provider => this;
|
||||
|
||||
public event EventHandler? Refreshed;
|
||||
public AsyncEventHandler Refreshed { get; } = new();
|
||||
|
||||
public bool IsCaseInsensitive { get; }
|
||||
|
||||
@@ -41,13 +46,14 @@ namespace FileTime.Providers.Local
|
||||
|
||||
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 rootContainer = RootContainers.FirstOrDefault(c => NormalizePath(c.Name) == NormalizePath(pathParts[0]));
|
||||
var rootContainer = _rootContainers.FirstOrDefault(c => NormalizePath(c.Name) == NormalizePath(pathParts[0]));
|
||||
|
||||
if (rootContainer == null)
|
||||
{
|
||||
@@ -55,27 +61,43 @@ namespace FileTime.Providers.Local
|
||||
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 CreateContainer(string name) => throw new NotSupportedException();
|
||||
public IElement CreateElement(string name) => throw new NotSupportedException();
|
||||
public bool IsExists(string name) => Items.Any(i => i.Name == name);
|
||||
public Task<IContainer> CreateContainer(string name) => throw new NotSupportedException();
|
||||
public Task<IElement> CreateElement(string name) => throw new NotSupportedException();
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,9 +34,10 @@ namespace FileTime.Providers.Local
|
||||
|
||||
public string GetPrimaryAttributeText() => _file.Length.ToSizeString();
|
||||
|
||||
public void Delete()
|
||||
public Task Delete()
|
||||
{
|
||||
_file.Delete();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Providers;
|
||||
|
||||
@@ -15,44 +16,11 @@ namespace FileTime.Providers.Local
|
||||
public LocalContentProvider Provider { get; }
|
||||
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 FullName { get; }
|
||||
|
||||
public event EventHandler? Refreshed;
|
||||
public AsyncEventHandler Refreshed { get; } = new();
|
||||
|
||||
public LocalFolder(DirectoryInfo directory, LocalContentProvider contentProvider, IContainer? parent)
|
||||
{
|
||||
@@ -66,7 +34,7 @@ namespace FileTime.Providers.Local
|
||||
|
||||
public IContainer? GetParent() => _parent;
|
||||
|
||||
public void Refresh()
|
||||
public Task Refresh()
|
||||
{
|
||||
_containers = new List<IContainer>();
|
||||
_elements = new List<IElement>();
|
||||
@@ -79,14 +47,32 @@ namespace FileTime.Providers.Local
|
||||
catch { }
|
||||
|
||||
_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 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)
|
||||
{
|
||||
@@ -95,29 +81,33 @@ namespace FileTime.Providers.Local
|
||||
|
||||
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;
|
||||
}
|
||||
public IContainer CreateContainer(string name)
|
||||
public async Task<IContainer> CreateContainer(string name)
|
||||
{
|
||||
Directory.CreateSubdirectory(name);
|
||||
Refresh();
|
||||
await Refresh();
|
||||
|
||||
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))) { }
|
||||
Refresh();
|
||||
await Refresh();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Interactions;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Providers;
|
||||
@@ -7,16 +8,11 @@ namespace FileTime.Providers.Smb
|
||||
public class SmbContentProvider : IContentProvider
|
||||
{
|
||||
private IContainer _parent;
|
||||
private readonly List<IContainer> _rootContainers;
|
||||
private readonly IInputInterface _inputInterface;
|
||||
|
||||
public IReadOnlyList<IContainer> RootContainers { get; }
|
||||
|
||||
public IReadOnlyList<IItem> Items => RootContainers;
|
||||
|
||||
public IReadOnlyList<IContainer> Containers => RootContainers;
|
||||
|
||||
public IReadOnlyList<IElement> Elements { get; } = new List<IElement>();
|
||||
private readonly List<IContainer> _rootContainers;
|
||||
private readonly IReadOnlyList<IContainer> _rootContainersReadOnly;
|
||||
private readonly IReadOnlyList<IItem>? _items;
|
||||
private readonly IReadOnlyList<IElement>? _elements = new List<IElement>().AsReadOnly();
|
||||
|
||||
public string Name { get; } = "smb";
|
||||
|
||||
@@ -26,16 +22,16 @@ namespace FileTime.Providers.Smb
|
||||
|
||||
public IContentProvider Provider => this;
|
||||
|
||||
public event EventHandler? Refreshed;
|
||||
public AsyncEventHandler Refreshed { get; } = new();
|
||||
|
||||
public SmbContentProvider(IInputInterface inputInterface)
|
||||
{
|
||||
_rootContainers = new List<IContainer>();
|
||||
RootContainers = _rootContainers.AsReadOnly();
|
||||
_rootContainersReadOnly = _rootContainers.AsReadOnly();
|
||||
_inputInterface = inputInterface;
|
||||
}
|
||||
|
||||
public IContainer CreateContainer(string name)
|
||||
public async Task<IContainer> CreateContainer(string name)
|
||||
{
|
||||
var fullName = "\\\\" + name;
|
||||
var container = _rootContainers.Find(c => c.Name == name);
|
||||
@@ -46,37 +42,54 @@ namespace FileTime.Providers.Smb
|
||||
_rootContainers.Add(container);
|
||||
}
|
||||
|
||||
Refresh();
|
||||
await Refresh();
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
public IElement CreateElement(string name)
|
||||
public Task<IElement> CreateElement(string name)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void Delete()
|
||||
public Task Delete()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public IItem? GetByPath(string path)
|
||||
public Task<IItem?> GetByPath(string path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,18 +16,15 @@ namespace FileTime.Providers.Smb
|
||||
|
||||
public IContentProvider Provider { get; }
|
||||
|
||||
private readonly Func<ISMBClient> _getSmbClient;
|
||||
|
||||
public SmbFile(string name, SmbContentProvider provider, IContainer parent, Func<ISMBClient> getSmbClient)
|
||||
public SmbFile(string name, SmbContentProvider provider, IContainer parent)
|
||||
{
|
||||
Name = name;
|
||||
FullName = parent.FullName + Constants.SeparatorChar + Name;
|
||||
|
||||
Provider = provider;
|
||||
_getSmbClient = getSmbClient;
|
||||
}
|
||||
|
||||
public void Delete()
|
||||
public Task Delete()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Providers;
|
||||
using SMBLibrary;
|
||||
@@ -10,43 +11,9 @@ namespace FileTime.Providers.Smb
|
||||
private IReadOnlyList<IItem>? _items;
|
||||
private IReadOnlyList<IContainer>? _containers;
|
||||
private IReadOnlyList<IElement>? _elements;
|
||||
private Func<ISMBClient> _getSmbClient;
|
||||
private readonly SmbShare _smbShare;
|
||||
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? FullName { get; }
|
||||
@@ -56,39 +23,33 @@ namespace FileTime.Providers.Smb
|
||||
public SmbContentProvider Provider { get; }
|
||||
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;
|
||||
_getSmbClient = getSmbClient;
|
||||
_smbShare = smbShare;
|
||||
|
||||
Name = name;
|
||||
FullName = parent?.FullName == null ? Name : parent.FullName + Constants.SeparatorChar + Name;
|
||||
Provider = contentProvider;
|
||||
_smbShare = smbShare;
|
||||
}
|
||||
|
||||
public IContainer CreateContainer(string name)
|
||||
public Task<IContainer> CreateContainer(string name)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IElement CreateElement(string name)
|
||||
public Task<IElement> CreateElement(string name)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Delete()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IItem? GetByPath(string path)
|
||||
public async Task<IItem?> GetByPath(string path)
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -97,7 +58,7 @@ namespace FileTime.Providers.Smb
|
||||
|
||||
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;
|
||||
@@ -105,12 +66,17 @@ namespace FileTime.Providers.Smb
|
||||
|
||||
public IContainer? GetParent() => _parent;
|
||||
|
||||
public bool IsExists(string name)
|
||||
public Task<bool> IsExists(string name)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
public Task Delete()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task Refresh()
|
||||
{
|
||||
var containers = new List<IContainer>();
|
||||
var elements = new List<IElement>();
|
||||
@@ -118,7 +84,7 @@ namespace FileTime.Providers.Smb
|
||||
try
|
||||
{
|
||||
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 { }
|
||||
|
||||
@@ -126,7 +92,23 @@ namespace FileTime.Providers.Smb
|
||||
_elements = elements.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Net;
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Interactions;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Providers;
|
||||
@@ -13,31 +14,11 @@ namespace FileTime.Providers.Smb
|
||||
private string? _password;
|
||||
|
||||
private IReadOnlyList<IContainer>? _shares;
|
||||
private IReadOnlyList<IItem>? _items;
|
||||
private readonly IReadOnlyList<IElement>? _elements = new List<IElement>().AsReadOnly();
|
||||
private ISMBClient? _client;
|
||||
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? FullName { get; }
|
||||
@@ -48,7 +29,7 @@ namespace FileTime.Providers.Smb
|
||||
|
||||
IContentProvider IItem.Provider => Provider;
|
||||
|
||||
public event EventHandler? Refreshed;
|
||||
public AsyncEventHandler Refreshed { get; } = new();
|
||||
|
||||
public SmbServer(string path, SmbContentProvider contentProvider, IInputInterface inputInterface)
|
||||
{
|
||||
@@ -58,43 +39,60 @@ namespace FileTime.Providers.Smb
|
||||
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();
|
||||
}
|
||||
|
||||
public IElement CreateElement(string name)
|
||||
public Task<IElement> CreateElement(string name)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
public IContainer? GetParent() => Provider;
|
||||
|
||||
public bool IsExists(string name)
|
||||
public Task<bool> IsExists(string name)
|
||||
{
|
||||
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);
|
||||
|
||||
_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)
|
||||
{
|
||||
@@ -108,7 +106,7 @@ namespace FileTime.Providers.Smb
|
||||
{
|
||||
if (_username == null && _password == null)
|
||||
{
|
||||
var inputs = _inputInterface.ReadInputs(
|
||||
var inputs = await _inputInterface.ReadInputs(
|
||||
new InputElement[]
|
||||
{
|
||||
new InputElement($"Username for '{Name}'", InputType.Text),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Interactions;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Providers;
|
||||
@@ -11,42 +12,9 @@ namespace FileTime.Providers.Smb
|
||||
private IReadOnlyList<IItem>? _items;
|
||||
private IReadOnlyList<IContainer>? _containers;
|
||||
private IReadOnlyList<IElement>? _elements;
|
||||
private Func<ISMBClient> _getSmbClient;
|
||||
private Func<Task<ISMBClient>> _getSmbClient;
|
||||
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? FullName { get; }
|
||||
@@ -56,9 +24,9 @@ namespace FileTime.Providers.Smb
|
||||
public SmbContentProvider Provider { get; }
|
||||
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;
|
||||
_getSmbClient = getSmbClient;
|
||||
@@ -68,26 +36,42 @@ namespace FileTime.Providers.Smb
|
||||
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();
|
||||
}
|
||||
|
||||
public IElement CreateElement(string name)
|
||||
public Task<IElement> CreateElement(string name)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Delete()
|
||||
public Task Delete()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IItem? GetByPath(string path)
|
||||
public async Task<IItem?> GetByPath(string path)
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -96,7 +80,7 @@ namespace FileTime.Providers.Smb
|
||||
|
||||
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;
|
||||
@@ -104,19 +88,19 @@ namespace FileTime.Providers.Smb
|
||||
|
||||
public IContainer? GetParent() => _parent;
|
||||
|
||||
public bool IsExists(string name)
|
||||
public Task<bool> IsExists(string name)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
public async Task Refresh()
|
||||
{
|
||||
var containers = new List<IContainer>();
|
||||
var elements = new List<IElement>();
|
||||
|
||||
try
|
||||
{
|
||||
(containers, elements) = ListFolder(this, Name, string.Empty);
|
||||
(containers, elements) = await ListFolder(this, Name, string.Empty);
|
||||
}
|
||||
catch { }
|
||||
|
||||
@@ -124,15 +108,15 @@ namespace FileTime.Providers.Smb
|
||||
_elements = elements.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 elements = new List<IElement>();
|
||||
|
||||
var client = _getSmbClient();
|
||||
var client = await _getSmbClient();
|
||||
ISMBFileStore fileStore = client.TreeConnect(shareName, out var status);
|
||||
if (status == NTStatus.STATUS_SUCCESS)
|
||||
{
|
||||
@@ -148,11 +132,11 @@ namespace FileTime.Providers.Smb
|
||||
{
|
||||
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
|
||||
{
|
||||
elements.Add(new SmbFile(fileDirectoryInformation.FileName, Provider, parent, _getSmbClient));
|
||||
elements.Add(new SmbFile(fileDirectoryInformation.FileName, Provider, parent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user