diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/GoToPathCommand.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/GoToPathCommand.cs new file mode 100644 index 0000000..4980f7b --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/GoToPathCommand.cs @@ -0,0 +1,13 @@ +namespace FileTime.App.Core.UserCommand; + +public class GoToPathCommand : IIdentifiableUserCommand +{ + public const string CommandName = "go_to_path"; + public static GoToPathCommand Instance { get; } = new GoToPathCommand(); + + private GoToPathCommand() + { + } + + public string UserCommandID => CommandName; +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ItemManipulationUserCommandHandlerService.cs b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ItemManipulationUserCommandHandlerService.cs index 2dcf5c9..0509008 100644 --- a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ItemManipulationUserCommandHandlerService.cs +++ b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ItemManipulationUserCommandHandlerService.cs @@ -130,7 +130,7 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi { var containerNameInput = new TextInputElement("Container name"); - await _inputInterface.ReadInputs(new List() { containerNameInput }); + await _inputInterface.ReadInputs(containerNameInput); //TODO: message on empty result var newContainerName = containerNameInput.Value; @@ -147,7 +147,7 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi { var containerNameInput = new TextInputElement("Element name"); - await _inputInterface.ReadInputs(new List() { containerNameInput }); + await _inputInterface.ReadInputs(containerNameInput); //TODO: message on empty result var newContainerName = containerNameInput.Value; diff --git a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/NavigationUserCommandHandlerService.cs b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/NavigationUserCommandHandlerService.cs index 1c2c5d7..95b228c 100644 --- a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/NavigationUserCommandHandlerService.cs +++ b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/NavigationUserCommandHandlerService.cs @@ -2,6 +2,7 @@ using FileTime.App.Core.Extensions; using FileTime.App.Core.Models.Enums; using FileTime.App.Core.UserCommand; using FileTime.App.Core.ViewModels; +using FileTime.Core.Interactions; using FileTime.Core.Models; using FileTime.Core.Services; using FileTime.Core.Timeline; @@ -18,6 +19,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase private readonly ILocalContentProvider _localContentProvider; private readonly IUserCommandHandlerService _userCommandHandlerService; private readonly ITimelessContentProvider _timelessContentProvider; + private readonly IInputInterface _inputInterface; private ITabViewModel? _selectedTab; private IContainer? _currentLocation; private IItemViewModel? _currentSelectedItem; @@ -29,13 +31,15 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase IServiceProvider serviceProvider, ILocalContentProvider localContentProvider, IUserCommandHandlerService userCommandHandlerService, - ITimelessContentProvider timelessContentProvider) : base(appState) + ITimelessContentProvider timelessContentProvider, + IInputInterface inputInterface) : base(appState) { _appState = appState; _serviceProvider = serviceProvider; _localContentProvider = localContentProvider; _userCommandHandlerService = userCommandHandlerService; _timelessContentProvider = timelessContentProvider; + _inputInterface = inputInterface; SaveSelectedTab(t => _selectedTab = t); SaveCurrentSelectedItem(i => _currentSelectedItem = i); @@ -50,6 +54,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase new TypeUserCommandHandler(EnterRapidTravel), new TypeUserCommandHandler(ExitRapidTravel), new TypeUserCommandHandler(GoToHome), + new TypeUserCommandHandler(GoToPath), new TypeUserCommandHandler(GoToProvider), new TypeUserCommandHandler(GoToRoot), new TypeUserCommandHandler(GoUp), @@ -66,6 +71,20 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase }); } + private async Task GoToPath() + { + var pathInput = new TextInputElement("Path"); + await _inputInterface.ReadInputs(pathInput); + + //TODO: message on empty result and on null pathInput.Value + var resolvedPath = await _timelessContentProvider.GetItemByNativePathAsync(new NativePath(pathInput.Value)); + if (resolvedPath is IContainer container) + { + await _userCommandHandlerService.HandleCommandAsync( + new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, container))); + } + } + private async Task GoToHome() { var path = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); diff --git a/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs b/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs index e990828..727dd64 100644 --- a/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs +++ b/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs @@ -19,6 +19,7 @@ public class DefaultIdentifiableCommandHandlerRegister : IStartupHandler AddUserCommand(EnterRapidTravelCommand.Instance); AddUserCommand(ExitRapidTravelCommand.Instance); AddUserCommand(GoToHomeCommand.Instance); + AddUserCommand(GoToPathCommand.Instance); AddUserCommand(GoToProviderCommand.Instance); AddUserCommand(GoToRootCommand.Instance); AddUserCommand(GoUpCommand.Instance); diff --git a/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentProvider.cs b/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentProvider.cs index d2b957f..8afe518 100644 --- a/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentProvider.cs +++ b/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentProvider.cs @@ -24,4 +24,5 @@ public interface IContentProvider : IContainer, IOnContainerEnter NativePath GetNativePath(FullName fullName); Task GetContentAsync(IElement element, int? maxLength = null, CancellationToken cancellationToken = default); + bool CanHandlePath(NativePath path); } \ No newline at end of file diff --git a/src/Core/FileTime.Core.Abstraction/Interactions/IInputInterface.cs b/src/Core/FileTime.Core.Abstraction/Interactions/IInputInterface.cs index 06efc14..aa2143c 100644 --- a/src/Core/FileTime.Core.Abstraction/Interactions/IInputInterface.cs +++ b/src/Core/FileTime.Core.Abstraction/Interactions/IInputInterface.cs @@ -2,5 +2,5 @@ namespace FileTime.Core.Interactions; public interface IInputInterface { - Task ReadInputs(IEnumerable fields); + Task ReadInputs(params IInputElement[] fields); } \ No newline at end of file diff --git a/src/Core/FileTime.Core.Abstraction/Timeline/ITimelessContentProvider.cs b/src/Core/FileTime.Core.Abstraction/Timeline/ITimelessContentProvider.cs index 356d56d..dba7a69 100644 --- a/src/Core/FileTime.Core.Abstraction/Timeline/ITimelessContentProvider.cs +++ b/src/Core/FileTime.Core.Abstraction/Timeline/ITimelessContentProvider.cs @@ -13,4 +13,6 @@ public interface ITimelessContentProvider bool forceResolve = false, AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown, ItemInitializationSettings itemInitializationSettings = default); + + Task GetItemByNativePathAsync(NativePath nativePath, PointInTime? pointInTime = null); } \ No newline at end of file diff --git a/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs b/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs index 5354ace..c77eaa9 100644 --- a/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs +++ b/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs @@ -81,4 +81,6 @@ public abstract class ContentProviderBase : IContentProvider public abstract Task GetContentAsync(IElement element, int? maxLength = null, CancellationToken cancellationToken = default); + + public abstract bool CanHandlePath(NativePath path); } \ No newline at end of file diff --git a/src/Core/FileTime.Core.Timeline/TimelessContentProvider.cs b/src/Core/FileTime.Core.Timeline/TimelessContentProvider.cs index 138db9c..7eb1bf3 100644 --- a/src/Core/FileTime.Core.Timeline/TimelessContentProvider.cs +++ b/src/Core/FileTime.Core.Timeline/TimelessContentProvider.cs @@ -2,7 +2,6 @@ using System.Reactive.Subjects; using FileTime.Core.ContentAccess; using FileTime.Core.Enums; using FileTime.Core.Models; -using FileTime.Core.Services; using Microsoft.Extensions.DependencyInjection; namespace FileTime.Core.Timeline; @@ -11,8 +10,7 @@ public class TimelessContentProvider : ITimelessContentProvider { private readonly Lazy> _contentProviders; - public BehaviorSubject CurrentPointInTime { get; } = - new BehaviorSubject(PointInTime.Present); + public BehaviorSubject CurrentPointInTime { get; } = new(PointInTime.Present); public TimelessContentProvider(IServiceProvider serviceProvider) { @@ -36,4 +34,16 @@ public class TimelessContentProvider : ITimelessContentProvider forceResolve, forceResolvePathType, itemInitializationSettings); } + + public async Task GetItemByNativePathAsync(NativePath nativePath, PointInTime? pointInTime = null) + { + foreach (var contentProvider in _contentProviders.Value) + { + if(!contentProvider.CanHandlePath(nativePath)) continue; + + return await contentProvider.GetItemByNativePathAsync(nativePath, pointInTime ?? PointInTime.Present); + } + + return null; + } } \ No newline at end of file diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/MainConfiguration.cs b/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/MainConfiguration.cs index 58a2e03..90cf14c 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/MainConfiguration.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/MainConfiguration.cs @@ -59,8 +59,8 @@ public static class MainConfiguration //new CommandBindingConfiguration(ConfigCommand.FindByName, new[] { Key.F, Key.N }), //new CommandBindingConfiguration(ConfigCommand.FindByNameRegex, new[] { Key.F, Key.R }), new CommandBindingConfiguration(GoToHomeCommand.CommandName, new[] { Key.G, Key.H }), - //new CommandBindingConfiguration(ConfigCommand.GoToPath, new KeyConfig(Key.L, ctrl: true)), - //new CommandBindingConfiguration(ConfigCommand.GoToPath, new[] { Key.G, Key.P }), + new CommandBindingConfiguration(GoToPathCommand.CommandName, new KeyConfig(Key.L, ctrl: true)), + new CommandBindingConfiguration(GoToPathCommand.CommandName, new[] { Key.G, Key.P }), new CommandBindingConfiguration(GoToProviderCommand.CommandName, new[] { Key.G, Key.T }), new CommandBindingConfiguration(GoToRootCommand.CommandName, new[] { Key.G, Key.R }), //new CommandBindingConfiguration(ConfigCommand.HardDelete, new[] { new KeyConfig(Key.D,shift: true), new KeyConfig(Key.D, shift: true) }), diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Services/DialogService.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Services/DialogService.cs index 6c1fdd0..6adfc14 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/Services/DialogService.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Services/DialogService.cs @@ -62,7 +62,7 @@ public class DialogService : IDialogService readInputsViewModel.CancelHandler?.Invoke(); } - public Task ReadInputs(IEnumerable fields) + public Task ReadInputs(params IInputElement[] fields) { var taskCompletionSource = new TaskCompletionSource(); ReadInputs(fields, () => taskCompletionSource.SetResult(true), () => taskCompletionSource.SetResult(false)); diff --git a/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs b/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs index c5bdbf5..5a8aad7 100644 --- a/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs +++ b/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs @@ -46,6 +46,22 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo }); } + public override bool CanHandlePath(NativePath path) + { + var rootDrive = _rootDirectories + .Items + .FirstOrDefault(r => + path.Path.StartsWith( + GetNativePath(r.Path).Path, + _isCaseInsensitive + ? StringComparison.InvariantCultureIgnoreCase + : StringComparison.InvariantCulture + ) + ); + + return rootDrive is not null; + } + public override Task GetItemByNativePathAsync(NativePath nativePath, PointInTime pointInTime, bool forceResolve = false, @@ -58,11 +74,11 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo { if ((path?.Length ?? 0) == 0) { - return Task.FromResult((IItem)this); + return Task.FromResult((IItem) this); } else if (Directory.Exists(path)) { - return Task.FromResult((IItem)DirectoryToContainer( + return Task.FromResult((IItem) DirectoryToContainer( new DirectoryInfo(path!.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar), pointInTime, !itemInitializationSettings.SkipChildInitialization) @@ -70,7 +86,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo } else if (File.Exists(path)) { - return Task.FromResult((IItem)FileToElement(new FileInfo(path), pointInTime)); + return Task.FromResult((IItem) FileToElement(new FileInfo(path), pointInTime)); } var type = forceResolvePathType switch @@ -102,10 +118,10 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo return forceResolvePathType switch { AbsolutePathType.Container => Task.FromResult( - (IItem)CreateEmptyContainer( + (IItem) CreateEmptyContainer( nativePath, pointInTime, - Observable.Return(new List() { innerException }) + Observable.Return(new List() {innerException}) ) ), AbsolutePathType.Element => Task.FromResult(CreateEmptyElement(nativePath)), @@ -220,7 +236,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo SourceCache? result = null; try { - var items = initializeChildren ? (List?)GetItemsByContainer(directoryInfo, pointInTime) : null; + var items = initializeChildren ? (List?) GetItemsByContainer(directoryInfo, pointInTime) : null; if (items != null) { result = new SourceCache(i => i.Path.Path); @@ -229,7 +245,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo } catch (Exception e) { - exceptions.OnNext(new List() { e }); + exceptions.OnNext(new List() {e}); } return Task.FromResult(result?.Connect()); @@ -303,7 +319,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo var size = maxLength ?? realFileSize switch { > int.MaxValue => int.MaxValue, - _ => (int)realFileSize + _ => (int) realFileSize }; var buffer = new byte[size]; await reader.ReadAsync(buffer, 0, size);