diff --git a/src/Core/FileTime.Core/Command/CopyCommand.cs b/src/Core/FileTime.Core/Command/CopyCommand.cs index bf84a5a..61fd2e4 100644 --- a/src/Core/FileTime.Core/Command/CopyCommand.cs +++ b/src/Core/FileTime.Core/Command/CopyCommand.cs @@ -9,7 +9,7 @@ namespace FileTime.Core.Command private Func>? _createContainer; private TimeRunner? _timeRunner; - public IList? Sources { get; } = new List(); + public IList Sources { get; } = new List(); public IContainer? Target { get; set; } diff --git a/src/Core/FileTime.Core/Command/ITransportationCommand.cs b/src/Core/FileTime.Core/Command/ITransportationCommand.cs index 61ac5c1..cbf5721 100644 --- a/src/Core/FileTime.Core/Command/ITransportationCommand.cs +++ b/src/Core/FileTime.Core/Command/ITransportationCommand.cs @@ -4,7 +4,7 @@ namespace FileTime.Core.Command { public interface ITransportationCommand : ICommand { - IList? Sources { get; } + IList Sources { get; } IContainer? Target { get; set;} TransportMode? TransportMode { get; set; } } diff --git a/src/Core/FileTime.Core/Command/MoveCommand.cs b/src/Core/FileTime.Core/Command/MoveCommand.cs index 2b85fdb..0530911 100644 --- a/src/Core/FileTime.Core/Command/MoveCommand.cs +++ b/src/Core/FileTime.Core/Command/MoveCommand.cs @@ -5,7 +5,7 @@ namespace FileTime.Core.Command { public class MoveCommand : ITransportationCommand { - public IList? Sources { get; } = new List(); + public IList Sources { get; } = new List(); public IContainer? Target { get; set; } public TransportMode? TransportMode { get; set; } = Command.TransportMode.Merge; diff --git a/src/Core/FileTime.Core/Components/Tab.cs b/src/Core/FileTime.Core/Components/Tab.cs index 62151b9..6174baa 100644 --- a/src/Core/FileTime.Core/Components/Tab.cs +++ b/src/Core/FileTime.Core/Components/Tab.cs @@ -6,7 +6,9 @@ namespace FileTime.Core.Components public class Tab { private IItem? _currentSelectedItem; +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. private IContainer _currentLocation; +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. private string? _lastPath; public int CurrentSelectedIndex { get; private set; } diff --git a/src/Core/FileTime.Core/Providers/TopContainer.cs b/src/Core/FileTime.Core/Providers/TopContainer.cs index 5430349..50833e3 100644 --- a/src/Core/FileTime.Core/Providers/TopContainer.cs +++ b/src/Core/FileTime.Core/Providers/TopContainer.cs @@ -12,14 +12,18 @@ namespace FileTime.Core.Providers private readonly IReadOnlyList? _items; private readonly IReadOnlyList? _elements = new List().AsReadOnly(); +#pragma warning disable CS8603 // Possible null reference return. public string Name => null; +#pragma warning restore CS8603 // Possible null reference return. public string? FullName => null; public bool IsHidden => false; public bool IsLoaded => true; +#pragma warning disable CS8603 // Possible null reference return. public IContentProvider Provider => null; +#pragma warning restore CS8603 // Possible null reference return. public bool CanDelete => false; public bool CanRename => false; diff --git a/src/Core/FileTime.Core/Timeline/TimeRunner.cs b/src/Core/FileTime.Core/Timeline/TimeRunner.cs index 9687942..9ebde23 100644 --- a/src/Core/FileTime.Core/Timeline/TimeRunner.cs +++ b/src/Core/FileTime.Core/Timeline/TimeRunner.cs @@ -74,7 +74,7 @@ namespace FileTime.Core.Timeline } }); - UpdateReadOnlyCommands(); + await UpdateReadOnlyCommands(); } public async Task TryStartCommandRunner() diff --git a/src/GuiApp/FileTime.Avalonia/App.axaml.cs b/src/GuiApp/FileTime.Avalonia/App.axaml.cs index aab6321..c440d3d 100644 --- a/src/GuiApp/FileTime.Avalonia/App.axaml.cs +++ b/src/GuiApp/FileTime.Avalonia/App.axaml.cs @@ -13,7 +13,7 @@ namespace FileTime.Avalonia { public static IServiceProvider ServiceProvider { get; private set; } - public App() + static App() { ServiceProvider ??= DependencyInjection .RegisterDefaultServices() diff --git a/src/GuiApp/FileTime.Avalonia/ViewModels/MainPageViewModel.cs b/src/GuiApp/FileTime.Avalonia/ViewModels/MainPageViewModel.cs index 181ee14..66a72a3 100644 --- a/src/GuiApp/FileTime.Avalonia/ViewModels/MainPageViewModel.cs +++ b/src/GuiApp/FileTime.Avalonia/ViewModels/MainPageViewModel.cs @@ -41,8 +41,8 @@ namespace FileTime.Avalonia.ViewModels private IClipboard _clipboard; private TimeRunner _timeRunner; - private IEnumerable? _contentProviders; - private Action? _inputHandler; + private IEnumerable _contentProviders; + private Func? _inputHandler; [Property] private string _text; @@ -68,7 +68,7 @@ namespace FileTime.Avalonia.ViewModels { _clipboard = App.ServiceProvider.GetService()!; _timeRunner = App.ServiceProvider.GetService()!; - _contentProviders = App.ServiceProvider.GetService>(); + _contentProviders = App.ServiceProvider.GetService>()!; var inputInterface = (BasicInputHandler)App.ServiceProvider.GetService()!; inputInterface.InputHandler = ReadInputs2; App.ServiceProvider.GetService(); @@ -107,12 +107,12 @@ namespace FileTime.Avalonia.ViewModels RootDriveInfos = driveInfos.OrderBy(d => d.Name).ToList(); } - private async Task GetContainerForWindowsDrive(DriveInfo drive) + private async Task GetContainerForWindowsDrive(DriveInfo drive) { return (await LocalContentProvider.GetRootContainers()).FirstOrDefault(d => d.Name == drive.Name.TrimEnd(Path.DirectorySeparatorChar)); } - private async Task GetContainerForLinuxDrive(DriveInfo drive) + private async Task GetContainerForLinuxDrive(DriveInfo drive) { return await LocalContentProvider.GetByPath(drive.Name) as IContainer; } @@ -274,13 +274,13 @@ namespace FileTime.Avalonia.ViewModels public Task CreateContainer() { - var handler = () => + var handler = async () => { if (Inputs != null) { var container = AppState.SelectedTab.CurrentLocation.Container; var createContainerCommand = new CreateContainerCommand(new Core.Models.AbsolutePath(container), Inputs[0].Value); - _timeRunner.AddCommand(createContainerCommand).Wait(); + await _timeRunner.AddCommand(createContainerCommand); Inputs = null; } }; @@ -292,13 +292,13 @@ namespace FileTime.Avalonia.ViewModels public Task CreateElement() { - var handler = () => + var handler = async () => { if (Inputs != null) { var container = AppState.SelectedTab.CurrentLocation.Container; var createElementCommand = new CreateElementCommand(new Core.Models.AbsolutePath(container), Inputs[0].Value); - _timeRunner.AddCommand(createElementCommand).Wait(); + await _timeRunner.AddCommand(createElementCommand); Inputs = null; } }; @@ -346,7 +346,7 @@ namespace FileTime.Avalonia.ViewModels public async Task Delete() { - IList? itemsToDelete = null; + IList? itemsToDelete = null; var askForDelete = false; var questionText = ""; var shouldDelete = false; @@ -403,11 +403,11 @@ namespace FileTime.Avalonia.ViewModels } else if (shouldDelete) { - HandleDelete(); + await HandleDelete(); } } - void HandleDelete() + async Task HandleDelete() { var deleteCommand = new DeleteCommand(); @@ -416,7 +416,7 @@ namespace FileTime.Avalonia.ViewModels deleteCommand.ItemsToDelete.Add(itemToDelete); } - _timeRunner.AddCommand(deleteCommand).Wait(); + await _timeRunner.AddCommand(deleteCommand); _clipboard.Clear(); } } @@ -465,12 +465,12 @@ namespace FileTime.Avalonia.ViewModels var selectedItem = AppState.SelectedTab.SelectedItem?.Item; if (selectedItem != null) { - var handler = () => + var handler = async () => { if (Inputs != null) { var renameCommand = new RenameCommand(new Core.Models.AbsolutePath(selectedItem), Inputs[0].Value); - _timeRunner.AddCommand(renameCommand).Wait(); + await _timeRunner.AddCommand(renameCommand); } }; @@ -502,23 +502,45 @@ namespace FileTime.Avalonia.ViewModels await _timeRunner.Refresh(); } - private async Task GoToContainer() + private Task GoToContainer() { - var handler = () => + var handler = async () => { if (Inputs != null) { - + var path = Inputs[0].Value; + foreach (var contentProvider in _contentProviders) + { + if (contentProvider.CanHandlePath(path)) + { + var possibleContainer = await contentProvider.GetByPath(path); + if (possibleContainer is IContainer container) + { + AppState.SelectedTab.OpenContainer(container).Wait(); + } + //TODO: multiple possible content provider handler + return; + } + } } }; ReadInputs(new List() { new Core.Interactions.InputElement("Path", InputType.Text) }, handler); + + return Task.CompletedTask; } [Command] - public void ProcessInputs() + public async void ProcessInputs() { - _inputHandler?.Invoke(); + try + { + if (_inputHandler != null) + { + await _inputHandler.Invoke(); + } + } + catch { } Inputs = null; _inputHandler = null; @@ -683,6 +705,10 @@ namespace FileTime.Avalonia.ViewModels } private void ReadInputs(List inputs, Action inputHandler) + { + ReadInputs(inputs, () => { inputHandler(); return Task.CompletedTask; }); + } + private void ReadInputs(List inputs, Func inputHandler) { Inputs = inputs.Select(i => new InputElementWrapper(i, i.DefaultValue)).ToList(); _inputHandler = inputHandler; @@ -692,13 +718,13 @@ namespace FileTime.Avalonia.ViewModels { var waiting = true; var result = new string[0]; - ReadInputs(fields.ToList(), () => - { - if(Inputs != null) + ReadInputs(fields.ToList(), () => + { + if (Inputs != null) { result = Inputs.Select(i => i.Value).ToArray(); } - waiting = false; + waiting = false; }); while (waiting) await Task.Delay(100); @@ -706,7 +732,7 @@ namespace FileTime.Avalonia.ViewModels return result; } - private void ShowMessageBox(string text, Action inputHandler) + private void ShowMessageBox(string text, Func inputHandler) { MessageBoxText = text; _inputHandler = inputHandler; diff --git a/src/GuiApp/FileTime.Avalonia/Views/MainWindow.axaml.cs b/src/GuiApp/FileTime.Avalonia/Views/MainWindow.axaml.cs index 5440b56..f07f727 100644 --- a/src/GuiApp/FileTime.Avalonia/Views/MainWindow.axaml.cs +++ b/src/GuiApp/FileTime.Avalonia/Views/MainWindow.axaml.cs @@ -42,7 +42,7 @@ namespace FileTime.Avalonia.Views { if (_inputElementWrapper == null) { - e.Handled = e.Handled || await ViewModel?.ProcessKeyDown(e.Key, e.KeyModifiers); + e.Handled = e.Handled || await ViewModel!.ProcessKeyDown(e.Key, e.KeyModifiers); } } @@ -50,7 +50,7 @@ namespace FileTime.Avalonia.Views { if (_inputElementWrapper == null) { - e.Handled = e.Handled || await ViewModel?.ProcessKeyUp(e.Key, e.KeyModifiers); + e.Handled = e.Handled || await ViewModel!.ProcessKeyUp(e.Key, e.KeyModifiers); } } diff --git a/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs b/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs index 6965e2f..7db04b8 100644 --- a/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs +++ b/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs @@ -48,11 +48,13 @@ namespace FileTime.Providers.Local public async Task GetByPath(string path) { + path = path.Replace(Path.DirectorySeparatorChar, Constants.SeparatorChar).TrimEnd(Constants.SeparatorChar); var pathParts = (IsCaseInsensitive ? path.ToLower() : path).TrimStart(Constants.SeparatorChar).Split(Constants.SeparatorChar); if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && pathParts.Length == 1 && pathParts[0] == "") return this; - var rootContainer = _rootContainers.FirstOrDefault(c => NormalizePath(c.Name) == NormalizePath(pathParts[0])); + var normalizedRootContainerName = NormalizePath(pathParts[0]); + var rootContainer = _rootContainers.FirstOrDefault(c => NormalizePath(c.Name) == normalizedRootContainerName); if (rootContainer == null) { @@ -77,7 +79,11 @@ namespace FileTime.Providers.Local 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) + { + var normalizedPath = NormalizePath(path); + return _rootContainers.Any(r => normalizedPath.StartsWith(NormalizePath(r.Name))); + } public void SetParent(IContainer container) => _parent = container; public Task> GetRootContainers(CancellationToken token = default) => Task.FromResult(_rootContainers);