Async core refactor
This commit is contained in:
@@ -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();
|
||||||
|
|
||||||
|
return SelectedItems.ContainsKey(currentLocation)
|
||||||
|
? SelectedItems[currentLocation]
|
||||||
: new List<TabItem>().AsReadOnly();
|
: new List<TabItem>().AsReadOnly();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@@ -70,9 +70,13 @@ namespace FileTime.ConsoleUI.App.UI.Color
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
Console.SetCursorPosition(0, 0);
|
Console.SetCursorPosition(0, 0);
|
||||||
Write(new string(' ', Console.WindowHeight * Console.WindowWidth));
|
Write(new string(' ', Console.WindowHeight * Console.WindowWidth));
|
||||||
}
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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!)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
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();
|
||||||
|
try
|
||||||
|
{
|
||||||
Console.CursorVisible = false;
|
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()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
Console.CursorLeft = 0;
|
Console.CursorLeft = 0;
|
||||||
Console.CursorTop = 0;
|
Console.CursorTop = 0;
|
||||||
@@ -66,12 +79,19 @@ namespace FileTime.ConsoleUI
|
|||||||
|
|
||||||
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>()
|
||||||
|
|||||||
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();
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ namespace FileTime.Core.Command
|
|||||||
{
|
{
|
||||||
public interface IExecutableCommand : ICommand
|
public interface IExecutableCommand : ICommand
|
||||||
{
|
{
|
||||||
void Execute();
|
Task Execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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),
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user