Timeless refactor

This commit is contained in:
2022-05-21 14:30:24 +02:00
parent ced0c88a10
commit 6ee5afa632
47 changed files with 571 additions and 181 deletions

View File

@@ -6,10 +6,10 @@ namespace FileTime.App.Core.Services;
public interface IClipboardService public interface IClipboardService
{ {
Type? CommandType { get; } Type? CommandType { get; }
IReadOnlyList<IAbsolutePath> Content { get; } IReadOnlyList<FullName> Content { get; }
void AddContent(IAbsolutePath absolutePath); void AddContent(FullName absolutePath);
void RemoveContent(IAbsolutePath absolutePath); void RemoveContent(FullName absolutePath);
void Clear(); void Clear();
void SetCommand<T>() where T : ITransportationCommand; void SetCommand<T>() where T : ITransportationCommand;
} }

View File

@@ -4,9 +4,9 @@ namespace FileTime.App.Core.UserCommand;
public class OpenContainerCommand : IUserCommand public class OpenContainerCommand : IUserCommand
{ {
public IAbsolutePath Path { get; } public AbsolutePath Path { get; }
public OpenContainerCommand(IAbsolutePath path) public OpenContainerCommand(AbsolutePath path)
{ {
Path = path; Path = path;
} }

View File

@@ -1,5 +1,7 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Reactive.Subjects;
using FileTime.App.Core.Models.Enums; using FileTime.App.Core.Models.Enums;
using FileTime.Core.Timeline;
namespace FileTime.App.Core.ViewModels; namespace FileTime.App.Core.ViewModels;

View File

@@ -15,7 +15,7 @@ public interface ITabViewModel : IInitable<ITab, int>
IObservable<IContainer?> CurrentLocation { get; } IObservable<IContainer?> CurrentLocation { get; }
IObservable<IItemViewModel?> CurrentSelectedItem { get; } IObservable<IItemViewModel?> CurrentSelectedItem { get; }
IObservable<IObservable<IChangeSet<IItemViewModel>>?> CurrentItems { get; } IObservable<IObservable<IChangeSet<IItemViewModel>>?> CurrentItems { get; }
IObservable<IChangeSet<IAbsolutePath>> MarkedItems { get; } IObservable<IChangeSet<FullName>> MarkedItems { get; }
IObservable<IObservable<IChangeSet<IItemViewModel>>?> SelectedsChildren { get; } IObservable<IObservable<IChangeSet<IItemViewModel>>?> SelectedsChildren { get; }
IObservable<IObservable<IChangeSet<IItemViewModel>>?> ParentsChildren { get; } IObservable<IObservable<IChangeSet<IItemViewModel>>?> ParentsChildren { get; }
BindedCollection<IItemViewModel>? CurrentItemsCollection { get; } BindedCollection<IItemViewModel>? CurrentItemsCollection { get; }
@@ -25,7 +25,7 @@ public interface ITabViewModel : IInitable<ITab, int>
IObservable<IReadOnlyCollection<IItemViewModel>?> ParentsChildrenCollectionObservable { get; } IObservable<IReadOnlyCollection<IItemViewModel>?> ParentsChildrenCollectionObservable { get; }
IObservable<IReadOnlyCollection<IItemViewModel>?> SelectedsChildrenCollectionObservable { get; } IObservable<IReadOnlyCollection<IItemViewModel>?> SelectedsChildrenCollectionObservable { get; }
void ClearMarkedItems(); void ClearMarkedItems();
void RemoveMarkedItem(IAbsolutePath item); void RemoveMarkedItem(FullName fullName);
void AddMarkedItem(IAbsolutePath item); void AddMarkedItem(FullName fullName);
void ToggleMarkedItem(IAbsolutePath item); void ToggleMarkedItem(FullName fullName);
} }

View File

@@ -1,13 +1,14 @@
using FileTime.App.Core.ViewModels; using FileTime.App.Core.ViewModels;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Timeline;
namespace FileTime.App.Core.Extensions; namespace FileTime.App.Core.Extensions;
public static class ViewModelExtensions public static class ViewModelExtensions
{ {
public static IAbsolutePath ToAbsolutePath(this IItemViewModel itemViewModel) public static AbsolutePath ToAbsolutePath(this IItemViewModel itemViewModel, ITimelessContentProvider timelessContentProvider)
{ {
var item = itemViewModel.BaseItem ?? throw new ArgumentException($"{nameof(itemViewModel)} does not have {nameof(IItemViewModel.BaseItem)}"); var item = itemViewModel.BaseItem ?? throw new ArgumentException($"{nameof(itemViewModel)} does not have {nameof(IItemViewModel.BaseItem)}");
return new AbsolutePath(item); return new AbsolutePath(timelessContentProvider, item);
} }
} }

View File

@@ -5,17 +5,17 @@ namespace FileTime.App.Core.Services;
public class ClipboardService : IClipboardService public class ClipboardService : IClipboardService
{ {
private List<IAbsolutePath> _content; private List<FullName> _content;
public IReadOnlyList<IAbsolutePath> Content { get; private set; } public IReadOnlyList<FullName> Content { get; private set; }
public Type? CommandType { get; private set; } public Type? CommandType { get; private set; }
public ClipboardService() public ClipboardService()
{ {
_content = new List<IAbsolutePath>(); _content = new List<FullName>();
Content = _content.AsReadOnly(); Content = _content.AsReadOnly();
} }
public void AddContent(IAbsolutePath absolutePath) public void AddContent(FullName absolutePath)
{ {
foreach (var content in _content) foreach (var content in _content)
{ {
@@ -25,7 +25,7 @@ public class ClipboardService : IClipboardService
_content.Add(absolutePath); _content.Add(absolutePath);
} }
public void RemoveContent(IAbsolutePath absolutePath) public void RemoveContent(FullName absolutePath)
{ {
for (var i = 0; i < _content.Count; i++) for (var i = 0; i < _content.Count; i++)
{ {
@@ -38,7 +38,7 @@ public class ClipboardService : IClipboardService
public void Clear() public void Clear()
{ {
_content = new List<IAbsolutePath>(); _content = new List<FullName>();
Content = _content.AsReadOnly(); Content = _content.AsReadOnly();
CommandType = null; CommandType = null;
} }

View File

@@ -4,8 +4,10 @@ using FileTime.App.Core.Models.Enums;
using FileTime.App.Core.UserCommand; using FileTime.App.Core.UserCommand;
using FileTime.App.Core.ViewModels; using FileTime.App.Core.ViewModels;
using FileTime.Core.Command; using FileTime.Core.Command;
using FileTime.Core.Command.CreateContainer;
using FileTime.Core.Interactions; using FileTime.Core.Interactions;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Timeline;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using CopyCommand = FileTime.Core.Command.Copy.CopyCommand; using CopyCommand = FileTime.Core.Command.Copy.CopyCommand;
@@ -19,24 +21,30 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
private readonly IClipboardService _clipboardService; private readonly IClipboardService _clipboardService;
private readonly IInputInterface _inputInterface; private readonly IInputInterface _inputInterface;
private readonly ILogger<ItemManipulationUserCommandHandlerService> _logger; private readonly ILogger<ItemManipulationUserCommandHandlerService> _logger;
private readonly BindedCollection<IAbsolutePath>? _markedItems; private readonly ITimelessContentProvider _timelessContentProvider;
private readonly BindedCollection<FullName>? _markedItems;
private PointInTime _currentPointInTime;
public ItemManipulationUserCommandHandlerService( public ItemManipulationUserCommandHandlerService(
IAppState appState, IAppState appState,
IUserCommandHandlerService userCommandHandlerService, IUserCommandHandlerService userCommandHandlerService,
IClipboardService clipboardService, IClipboardService clipboardService,
IInputInterface inputInterface, IInputInterface inputInterface,
ILogger<ItemManipulationUserCommandHandlerService> logger) : base(appState) ILogger<ItemManipulationUserCommandHandlerService> logger,
ITimelessContentProvider timelessContentProvider) : base(appState, timelessContentProvider)
{ {
_userCommandHandlerService = userCommandHandlerService; _userCommandHandlerService = userCommandHandlerService;
_clipboardService = clipboardService; _clipboardService = clipboardService;
_inputInterface = inputInterface; _inputInterface = inputInterface;
_logger = logger; _logger = logger;
_timelessContentProvider = timelessContentProvider;
_currentPointInTime = null!;
SaveSelectedTab(t => _selectedTab = t); SaveSelectedTab(t => _selectedTab = t);
SaveCurrentSelectedItem(i => _currentSelectedItem = i); SaveCurrentSelectedItem(i => _currentSelectedItem = i);
SaveCurrentPointInTime(t => _currentPointInTime = t);
_markedItems = new BindedCollection<IAbsolutePath>(appState.SelectedTab.Select(t => t?.MarkedItems)); _markedItems = new BindedCollection<FullName>(appState.SelectedTab.Select(t => t?.MarkedItems));
AddCommandHandlers(new IUserCommandHandler[] AddCommandHandlers(new IUserCommandHandler[]
{ {
@@ -51,7 +59,7 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
{ {
if (_selectedTab == null || _currentSelectedItem?.BaseItem?.FullName == null) return; if (_selectedTab == null || _currentSelectedItem?.BaseItem?.FullName == null) return;
_selectedTab.ToggleMarkedItem(new AbsolutePath(_currentSelectedItem.BaseItem)); _selectedTab.ToggleMarkedItem(_currentSelectedItem.BaseItem.FullName);
await _userCommandHandlerService.HandleCommandAsync(MoveCursorDownCommand.Instance); await _userCommandHandlerService.HandleCommandAsync(MoveCursorDownCommand.Instance);
} }
@@ -71,7 +79,8 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
} }
else if (_currentSelectedItem?.BaseItem != null) else if (_currentSelectedItem?.BaseItem != null)
{ {
_clipboardService.AddContent(new AbsolutePath(_currentSelectedItem.BaseItem)); var item = _currentSelectedItem.BaseItem;
_clipboardService.AddContent(item.FullName ?? throw new ArgumentException($"{nameof(item.FullName)} can not be null.", nameof(item)));
} }
return Task.CompletedTask; return Task.CompletedTask;
@@ -117,5 +126,7 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
//TODO: message on empty result //TODO: message on empty result
var newContainerName = containerNameInput.Value; var newContainerName = containerNameInput.Value;
var command = new CreateContainerCommand();
} }
} }

View File

@@ -4,6 +4,7 @@ using FileTime.App.Core.UserCommand;
using FileTime.App.Core.ViewModels; using FileTime.App.Core.ViewModels;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Services; using FileTime.Core.Services;
using FileTime.Core.Timeline;
using FileTime.Providers.Local; using FileTime.Providers.Local;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@@ -15,6 +16,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
private readonly ILocalContentProvider _localContentProvider; private readonly ILocalContentProvider _localContentProvider;
private readonly IUserCommandHandlerService _userCommandHandlerService; private readonly IUserCommandHandlerService _userCommandHandlerService;
private readonly ITimelessContentProvider _timelessContentProvider;
private ITabViewModel? _selectedTab; private ITabViewModel? _selectedTab;
private IContainer? _currentLocation; private IContainer? _currentLocation;
private IItemViewModel? _currentSelectedItem; private IItemViewModel? _currentSelectedItem;
@@ -25,12 +27,14 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
IAppState appState, IAppState appState,
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
ILocalContentProvider localContentProvider, ILocalContentProvider localContentProvider,
IUserCommandHandlerService userCommandHandlerService) : base(appState) IUserCommandHandlerService userCommandHandlerService,
ITimelessContentProvider timelessContentProvider) : base(appState)
{ {
_appState = appState; _appState = appState;
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
_localContentProvider = localContentProvider; _localContentProvider = localContentProvider;
_userCommandHandlerService = userCommandHandlerService; _userCommandHandlerService = userCommandHandlerService;
_timelessContentProvider = timelessContentProvider;
SaveSelectedTab(t => _selectedTab = t); SaveSelectedTab(t => _selectedTab = t);
SaveCurrentSelectedItem(i => _currentSelectedItem = i); SaveCurrentSelectedItem(i => _currentSelectedItem = i);
@@ -71,7 +75,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
private async Task GoUp() private async Task GoUp()
{ {
if (_currentLocation?.Parent is not IAbsolutePath parentPath || await parentPath.ResolveAsyncSafe() is not IContainer newContainer) return; if (_currentLocation?.Parent is not AbsolutePath parentPath || await parentPath.ResolveAsyncSafe() is not IContainer newContainer) return;
_selectedTab?.Tab?.SetCurrentLocation(newContainer); _selectedTab?.Tab?.SetCurrentLocation(newContainer);
} }
@@ -94,7 +98,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
var newSelectedItem = getNewSelected(_currentItems); var newSelectedItem = getNewSelected(_currentItems);
if (newSelectedItem == null) return; if (newSelectedItem == null) return;
_selectedTab.Tab?.SetSelectedItem(newSelectedItem.ToAbsolutePath()); _selectedTab.Tab?.SetSelectedItem(newSelectedItem.ToAbsolutePath(_timelessContentProvider));
} }
private Task EnterRapidTravel() private Task EnterRapidTravel()

View File

@@ -2,6 +2,7 @@ using System.Reactive.Linq;
using DynamicData; using DynamicData;
using FileTime.App.Core.ViewModels; using FileTime.App.Core.ViewModels;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Timeline;
namespace FileTime.App.Core.Services.UserCommandHandler; namespace FileTime.App.Core.Services.UserCommandHandler;
@@ -9,10 +10,14 @@ public abstract class UserCommandHandlerServiceBase : IUserCommandHandler
{ {
private readonly List<IUserCommandHandler> _userCommandHandlers = new(); private readonly List<IUserCommandHandler> _userCommandHandlers = new();
private readonly IAppState? _appState; private readonly IAppState? _appState;
private readonly ITimelessContentProvider? _timelessContentProvider;
protected UserCommandHandlerServiceBase(IAppState? appState = null) protected UserCommandHandlerServiceBase(
IAppState? appState = null,
ITimelessContentProvider? timelessContentProvider = null)
{ {
_appState = appState; _appState = appState;
_timelessContentProvider = timelessContentProvider;
} }
public bool CanHandleCommand(UserCommand.IUserCommand command) => _userCommandHandlers.Any(h => h.CanHandleCommand(command)); public bool CanHandleCommand(UserCommand.IUserCommand command) => _userCommandHandlers.Any(h => h.CanHandleCommand(command));
@@ -48,13 +53,23 @@ public abstract class UserCommandHandlerServiceBase : IUserCommandHandler
protected IDisposable SaveCurrentItems(Action<IEnumerable<IItemViewModel>> handler) protected IDisposable SaveCurrentItems(Action<IEnumerable<IItemViewModel>> handler)
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t?.CurrentItemsCollectionObservable ?? Observable.Return((IEnumerable<IItemViewModel>?) Enumerable.Empty<IItemViewModel>())).Switch().Subscribe(i => handler(i ?? Enumerable.Empty<IItemViewModel>()))); => RunWithAppState(appState => appState.SelectedTab.Select(t => t?.CurrentItemsCollectionObservable ?? Observable.Return((IEnumerable<IItemViewModel>?) Enumerable.Empty<IItemViewModel>())).Switch().Subscribe(i => handler(i ?? Enumerable.Empty<IItemViewModel>())));
protected IDisposable SaveMarkedItems(Action<IChangeSet<IAbsolutePath>> handler) protected IDisposable SaveMarkedItems(Action<IChangeSet<FullName>> handler)
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t == null ? Observable.Empty<IChangeSet<IAbsolutePath>>() : t.MarkedItems).Switch().Subscribe(handler)); => RunWithAppState(appState => appState.SelectedTab.Select(t => t == null ? Observable.Empty<IChangeSet<FullName>>() : t.MarkedItems).Switch().Subscribe(handler));
protected IDisposable SaveCurrentPointInTime(Action<PointInTime> handler)
=> RunWithTimelessContentProvider(timelessContentProvider => timelessContentProvider.CurrentPointInTime.Subscribe(handler));
private IDisposable RunWithAppState(Func<IAppState, IDisposable> act) private IDisposable RunWithAppState(Func<IAppState, IDisposable> act)
{ {
if (_appState == null) throw new NullReferenceException($"AppState is nit initialized in {nameof(UserCommandHandlerServiceBase)}."); if (_appState == null) throw new NullReferenceException($"AppState is not initialized in {nameof(UserCommandHandlerServiceBase)}.");
return act(_appState); return act(_appState);
} }
private IDisposable RunWithTimelessContentProvider(Func<ITimelessContentProvider, IDisposable> act)
{
if (_timelessContentProvider == null) throw new NullReferenceException($"TimelessContainer is not initialized in {nameof(UserCommandHandlerServiceBase)}.");
return act(_timelessContentProvider);
}
} }

View File

@@ -3,6 +3,7 @@ using System.Reactive.Linq;
using System.Reactive.Subjects; using System.Reactive.Subjects;
using DynamicData; using DynamicData;
using FileTime.App.Core.Models.Enums; using FileTime.App.Core.Models.Enums;
using FileTime.Core.Timeline;
using MvvmGen; using MvvmGen;
using MoreLinq; using MoreLinq;

View File

@@ -57,7 +57,7 @@ public abstract partial class ItemViewModel : IItemViewModel
DisplayNameText = item.DisplayName; DisplayNameText = item.DisplayName;
IsMarked = itemViewModelType is ItemViewModelType.Main IsMarked = itemViewModelType is ItemViewModelType.Main
? parentTab.MarkedItems.ToCollection().Select(m => m.Any(i => i.Path.Path == item.FullName?.Path)) ? parentTab.MarkedItems.ToCollection().Select(m => m.Any(i => i.Path == item.FullName?.Path))
: Observable.Return(false); : Observable.Return(false);
IsSelected = itemViewModelType is ItemViewModelType.Main IsSelected = itemViewModelType is ItemViewModelType.Main

View File

@@ -19,7 +19,7 @@ public partial class TabViewModel : ITabViewModel, IDisposable
private readonly IItemNameConverterService _itemNameConverterService; private readonly IItemNameConverterService _itemNameConverterService;
private readonly IAppState _appState; private readonly IAppState _appState;
private readonly IRxSchedulerService _rxSchedulerService; private readonly IRxSchedulerService _rxSchedulerService;
private readonly SourceList<IAbsolutePath> _markedItems = new(); private readonly SourceList<FullName> _markedItems = new();
private readonly List<IDisposable> _disposables = new(); private readonly List<IDisposable> _disposables = new();
private bool _disposed; private bool _disposed;
@@ -31,7 +31,7 @@ public partial class TabViewModel : ITabViewModel, IDisposable
public IObservable<IContainer?> CurrentLocation { get; private set; } = null!; public IObservable<IContainer?> CurrentLocation { get; private set; } = null!;
public IObservable<IItemViewModel?> CurrentSelectedItem { get; private set; } = null!; public IObservable<IItemViewModel?> CurrentSelectedItem { get; private set; } = null!;
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> CurrentItems { get; private set; } = null!; public IObservable<IObservable<IChangeSet<IItemViewModel>>?> CurrentItems { get; private set; } = null!;
public IObservable<IChangeSet<IAbsolutePath>> MarkedItems { get; } public IObservable<IChangeSet<FullName>> MarkedItems { get; }
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> SelectedsChildren { get; private set; } = null!; public IObservable<IObservable<IChangeSet<IItemViewModel>>?> SelectedsChildren { get; private set; } = null!;
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> ParentsChildren { get; private set; } = null!; public IObservable<IObservable<IChangeSet<IItemViewModel>>?> ParentsChildren { get; private set; } = null!;
@@ -171,7 +171,7 @@ public partial class TabViewModel : ITabViewModel, IDisposable
} }
} }
private static async Task<IItem> MapItem(IAbsolutePath item) private static async Task<IItem> MapItem(AbsolutePath item)
=> await item.ResolveAsync(forceResolve: true, => await item.ResolveAsync(forceResolve: true,
itemInitializationSettings: new ItemInitializationSettings(true)); itemInitializationSettings: new ItemInitializationSettings(true));
@@ -211,24 +211,24 @@ public partial class TabViewModel : ITabViewModel, IDisposable
throw new ArgumentException($"{nameof(item)} is not {nameof(IContainer)} neither {nameof(IElement)}"); throw new ArgumentException($"{nameof(item)} is not {nameof(IContainer)} neither {nameof(IElement)}");
} }
public void AddMarkedItem(IAbsolutePath item) => _markedItems.Add(item); public void AddMarkedItem(FullName fullName) => _markedItems.Add(fullName);
public void RemoveMarkedItem(IAbsolutePath item) public void RemoveMarkedItem(FullName fullName)
{ {
var itemsToRemove = _markedItems.Items.Where(i => i.Path.Path == item.Path.Path).ToList(); var itemsToRemove = _markedItems.Items.Where(i => i.Path == fullName.Path).ToList();
_markedItems.RemoveMany(itemsToRemove); _markedItems.RemoveMany(itemsToRemove);
} }
public void ToggleMarkedItem(IAbsolutePath item) public void ToggleMarkedItem(FullName fullName)
{ {
if (_markedItems.Items.Any(i => i.Path.Path == item.Path.Path)) if (_markedItems.Items.Any(i => i.Path == fullName.Path))
{ {
RemoveMarkedItem(item); RemoveMarkedItem(fullName);
} }
else else
{ {
AddMarkedItem(item); AddMarkedItem(fullName);
} }
} }

View File

@@ -1,5 +1,6 @@
using FileTime.App.Core; using FileTime.App.Core;
using FileTime.Core.Services; using FileTime.Core.Services;
using FileTime.Core.Timeline;
using FileTime.Providers.Local; using FileTime.Providers.Local;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@@ -13,6 +14,8 @@ public static class DependencyInjection
return serviceCollection return serviceCollection
.AddTransient<ITab, Tab>() .AddTransient<ITab, Tab>()
.AddSingleton<ICommandScheduler, CommandScheduler>()
.AddSingleton<ITimelessContentProvider, TimelessContentProvider>()
.AddCoreAppServices() .AddCoreAppServices()
.AddLocalServices(); .AddLocalServices();
} }

View File

@@ -14,6 +14,7 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Core\FileTime.Core.Timeline\FileTime.Core.Timeline.csproj" />
<ProjectReference Include="..\..\Providers\FileTime.Providers.Local\FileTime.Providers.Local.csproj" /> <ProjectReference Include="..\..\Providers\FileTime.Providers.Local\FileTime.Providers.Local.csproj" />
<ProjectReference Include="..\FileTime.App.Core\FileTime.App.Core.csproj" /> <ProjectReference Include="..\FileTime.App.Core\FileTime.App.Core.csproj" />
</ItemGroup> </ItemGroup>

View File

@@ -0,0 +1,8 @@
namespace FileTime.Core.Command;
public enum CanCommandRun
{
True,
False,
Forcable
}

View File

@@ -1,6 +1,9 @@
using FileTime.Core.Timeline;
namespace FileTime.Core.Command; namespace FileTime.Core.Command;
public interface ICommand public interface ICommand
{ {
Task<CanCommandRun> CanRun(PointInTime currentTime);
Task<PointInTime?> SimulateCommand(PointInTime? currentTime);
} }

View File

@@ -0,0 +1,8 @@
using FileTime.Core.Timeline;
namespace FileTime.Core.Command;
public interface IExecutableCommand : ICommand
{
Task Execute(ICommandScheduler commandScheduler);
}

View File

@@ -0,0 +1,53 @@
using FileTime.Core.Enums;
using FileTime.Core.Timeline;
namespace FileTime.Core.Models;
public class AbsolutePath
{
public ITimelessContentProvider TimelessProvider { get; }
public PointInTime PointInTime { get; }
public FullName Path { get; }
public AbsolutePathType Type { get; }
public AbsolutePath(
ITimelessContentProvider timelessProvider,
PointInTime pointInTime,
FullName path,
AbsolutePathType type)
{
TimelessProvider = timelessProvider;
Path = path;
Type = type;
PointInTime = pointInTime;
}
public AbsolutePath(ITimelessContentProvider timelessProvider, IItem item)
{
TimelessProvider = timelessProvider;
PointInTime = item.PointInTime;
Path = item.FullName ?? throw new ArgumentException($"{nameof(item.FullName)} can not be null.", nameof(item));
Type = item.Type;
}
public async Task<IItem> ResolveAsync(bool forceResolve = false,
ItemInitializationSettings itemInitializationSettings = default)
{
return await TimelessProvider.GetItemByFullNameAsync(Path, PointInTime, forceResolve, Type,
itemInitializationSettings);
}
public async Task<IItem?> ResolveAsyncSafe(bool forceResolve = false,
ItemInitializationSettings itemInitializationSettings = default)
{
try
{
return await ResolveAsync(forceResolve, itemInitializationSettings);
}
catch
{
return null;
}
}
}

View File

@@ -1,15 +0,0 @@
using FileTime.Core.Enums;
using FileTime.Core.Services;
namespace FileTime.Core.Models;
public interface IAbsolutePath
{
IContentProvider ContentProvider { get; }
IContentProvider? VirtualContentProvider { get; }
FullName Path { get; }
AbsolutePathType Type { get; }
Task<IItem> ResolveAsync(bool forceResolve = false, ItemInitializationSettings itemInitializationSettings = default);
Task<IItem?> ResolveAsyncSafe(bool forceResolve = false, ItemInitializationSettings itemInitializationSettings = default);
}

View File

@@ -4,6 +4,6 @@ namespace FileTime.Core.Models;
public interface IContainer : IItem public interface IContainer : IItem
{ {
IObservable<IObservable<IChangeSet<IAbsolutePath>>?> Items { get; } IObservable<IObservable<IChangeSet<AbsolutePath>>?> Items { get; }
IObservable<bool> IsLoading { get; } IObservable<bool> IsLoading { get; }
} }

View File

@@ -1,6 +1,7 @@
using System.Reactive.Linq; using System.Reactive.Linq;
using FileTime.Core.Enums; using FileTime.Core.Enums;
using FileTime.Core.Services; using FileTime.Core.Services;
using FileTime.Core.Timeline;
namespace FileTime.Core.Models; namespace FileTime.Core.Models;
@@ -10,7 +11,7 @@ public interface IItem
string DisplayName { get; } string DisplayName { get; }
FullName? FullName { get; } FullName? FullName { get; }
NativePath? NativePath { get; } NativePath? NativePath { get; }
IAbsolutePath? Parent { get; } AbsolutePath? Parent { get; }
bool IsHidden { get; } bool IsHidden { get; }
bool IsExists { get; } bool IsExists { get; }
DateTime? CreatedAt { get; } DateTime? CreatedAt { get; }
@@ -19,6 +20,7 @@ public interface IItem
IContentProvider Provider { get; } IContentProvider Provider { get; }
string? Attributes { get; } string? Attributes { get; }
AbsolutePathType Type { get; } AbsolutePathType Type { get; }
PointInTime PointInTime { get; }
IObservable<IEnumerable<Exception>> Exceptions { get; } IObservable<IEnumerable<Exception>> Exceptions { get; }
ReadOnlyExtensionCollection Extensions { get; } ReadOnlyExtensionCollection Extensions { get; }

View File

@@ -1,6 +1,7 @@
using FileTime.Core.Behaviors; using FileTime.Core.Behaviors;
using FileTime.Core.Enums; using FileTime.Core.Enums;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Timeline;
namespace FileTime.Core.Services; namespace FileTime.Core.Services;
@@ -8,17 +9,18 @@ public interface IContentProvider : IContainer, IOnContainerEnter
{ {
Task<IItem> GetItemByFullNameAsync( Task<IItem> GetItemByFullNameAsync(
FullName fullName, FullName fullName,
PointInTime pointInTime,
bool forceResolve = false, bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown, AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default); ItemInitializationSettings itemInitializationSettings = default);
Task<IItem> GetItemByNativePathAsync( Task<IItem> GetItemByNativePathAsync(NativePath nativePath,
NativePath nativePath, PointInTime pointInTime,
bool forceResolve = false, bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown, AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default); ItemInitializationSettings itemInitializationSettings = default);
Task<List<IAbsolutePath>> GetItemsByContainerAsync(FullName fullName); Task<List<AbsolutePath>> GetItemsByContainerAsync(FullName fullName, PointInTime pointInTime);
NativePath GetNativePath(FullName fullName); NativePath GetNativePath(FullName fullName);
Task<byte[]?> GetContentAsync(IElement element, int? maxLength = null, CancellationToken cancellationToken = default); Task<byte[]?> GetContentAsync(IElement element, int? maxLength = null, CancellationToken cancellationToken = default);

View File

@@ -7,12 +7,12 @@ namespace FileTime.Core.Services;
public interface ITab : IInitable<IContainer> public interface ITab : IInitable<IContainer>
{ {
IObservable<IContainer?> CurrentLocation { get; } IObservable<IContainer?> CurrentLocation { get; }
IObservable<IAbsolutePath?> CurrentSelectedItem { get; } IObservable<AbsolutePath?> CurrentSelectedItem { get; }
IObservable<IObservable<IChangeSet<IItem>>?> CurrentItems { get; } IObservable<IObservable<IChangeSet<IItem>>?> CurrentItems { get; }
void SetCurrentLocation(IContainer newLocation); void SetCurrentLocation(IContainer newLocation);
void AddItemFilter(ItemFilter filter); void AddItemFilter(ItemFilter filter);
void RemoveItemFilter(ItemFilter filter); void RemoveItemFilter(ItemFilter filter);
void RemoveItemFilter(string name); void RemoveItemFilter(string name);
void SetSelectedItem(IAbsolutePath newSelectedItem); void SetSelectedItem(AbsolutePath newSelectedItem);
} }

View File

@@ -0,0 +1,21 @@
using FileTime.Core.Command;
namespace FileTime.Core.Timeline;
public class CommandTimeState
{
public ICommand Command { get; }
public CanCommandRun CanRun { get; private set; } = CanCommandRun.False;
public bool ForceRun { get; set; }
public CommandTimeState(ICommand command, PointInTime? startTime)
{
Command = command;
Task.Run(async () => await UpdateState(startTime)).Wait();
}
public async Task UpdateState(PointInTime? startPoint)
{
CanRun = startPoint == null ? CanCommandRun.False : await Command.CanRun(startPoint);
}
}

View File

@@ -0,0 +1,16 @@
using FileTime.Core.Models;
using FileTime.Core.Services;
namespace FileTime.Core.Timeline;
public class Difference
{
public AbsolutePath AbsolutePath { get; }
public DifferenceActionType Action { get; }
public Difference(DifferenceActionType action, AbsolutePath absolutePath)
{
AbsolutePath = absolutePath;
Action = action;
}
}

View File

@@ -0,0 +1,7 @@
namespace FileTime.Core.Timeline;
public enum DifferenceActionType
{
Create,
Delete
}

View File

@@ -0,0 +1,6 @@
namespace FileTime.Core.Timeline;
public interface ICommandScheduler
{
}

View File

@@ -0,0 +1,16 @@
using System.Reactive.Subjects;
using FileTime.Core.Enums;
using FileTime.Core.Models;
namespace FileTime.Core.Timeline;
public interface ITimelessContentProvider
{
BehaviorSubject<PointInTime> CurrentPointInTime { get; }
Task<IItem> GetItemByFullNameAsync(FullName fullName,
PointInTime? pointInTime,
bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default);
}

View File

@@ -0,0 +1,90 @@
using FileTime.Core.Command;
namespace FileTime.Core.Timeline;
public class ParallelCommands
{
private static ushort _idCounter;
public List<CommandTimeState> _commands;
public ushort Id { get; }
public IReadOnlyList<CommandTimeState> Commands { get; }
public PointInTime? Result { get; private set; }
public ParallelCommands(PointInTime? result)
: this(new List<CommandTimeState>(), result)
{
}
private ParallelCommands(List<CommandTimeState> commands, PointInTime? result)
{
Id = _idCounter++;
_commands = commands;
Commands = _commands.AsReadOnly();
Result = result;
}
public static async Task<ParallelCommands> Create(PointInTime? startTime, IEnumerable<ICommand> commands)
{
var commandStates = new List<CommandTimeState>();
var currentTime = startTime;
foreach (var command in commands)
{
CommandTimeState commandTimeState = new(command, currentTime);
if (currentTime != null)
{
var canRun = await command.CanRun(currentTime);
if (canRun == CanCommandRun.True)
{
currentTime = await command.SimulateCommand(currentTime);
}
else
{
currentTime = null;
}
}
commandStates.Add(commandTimeState);
}
return new ParallelCommands(commandStates, currentTime);
}
public async Task AddCommand(ICommand command)
{
_commands.Add(new CommandTimeState(command, Result));
if (Result != null)
{
Result = await command.SimulateCommand(Result);
}
}
public async Task<PointInTime?> RefreshResult(PointInTime? startPoint)
{
var result = startPoint;
foreach (var command in _commands)
{
await command.UpdateState(result);
if (result != null)
{
var canRun = await command.Command.CanRun(result);
if (canRun == CanCommandRun.True || (canRun == CanCommandRun.Forcable && command.ForceRun))
{
result = await command.Command.SimulateCommand(result);
}
else
{
result = null;
}
}
}
Result = result;
return Result;
}
public void RemoveAt(int number) => _commands.RemoveAt(number);
internal void Remove(CommandTimeState command) => _commands.Remove(command);
}

View File

@@ -0,0 +1,41 @@
namespace FileTime.Core.Timeline;
public class PointInTime
{
private readonly List<Difference> _differences;
public static readonly PointInTime Eternal = new PointInTime();
public static readonly PointInTime Present = new PointInTime();
public IReadOnlyList<Difference> Differences { get; }
private PointInTime() : this(new List<Difference>())
{
}
private PointInTime(IEnumerable<Difference> differences)
{
_differences = new List<Difference>(differences);
Differences = _differences.AsReadOnly();
}
private PointInTime(PointInTime previous, IEnumerable<Difference> differences)
: this(MergeDifferences(previous.Differences, differences))
{
}
public PointInTime WithDifferences(IEnumerable<Difference> differences) =>
new(this, differences);
private static List<Difference> MergeDifferences(IEnumerable<Difference> previouses,
IEnumerable<Difference> differences)
{
var merged = new List<Difference>();
merged.AddRange(previouses);
merged.AddRange(differences);
return merged;
}
public static PointInTime CreateEmpty() => new PointInTime();
}

View File

@@ -1,6 +1,16 @@
using FileTime.Core.Timeline;
namespace FileTime.Core.Command.Copy; namespace FileTime.Core.Command.Copy;
public class CopyCommand : ITransportationCommand public class CopyCommand : ITransportationCommand
{ {
public Task<CanCommandRun> CanRun(PointInTime currentTime)
{
throw new NotImplementedException();
}
public Task<PointInTime?> SimulateCommand(PointInTime? currentTime)
{
throw new NotImplementedException();
}
} }

View File

@@ -0,0 +1,21 @@
using FileTime.Core.Timeline;
namespace FileTime.Core.Command.CreateContainer;
public class CreateContainerCommand : IExecutableCommand
{
public Task<CanCommandRun> CanRun(PointInTime currentTime)
{
throw new NotImplementedException();
}
public Task<PointInTime?> SimulateCommand(PointInTime? currentTime)
{
throw new NotImplementedException();
}
public Task Execute(ICommandScheduler commandScheduler)
{
throw new NotImplementedException();
}
}

View File

@@ -1,44 +0,0 @@
using FileTime.Core.Enums;
using FileTime.Core.Services;
namespace FileTime.Core.Models;
public class AbsolutePath : IAbsolutePath
{
public IContentProvider ContentProvider { get; }
public IContentProvider? VirtualContentProvider { get; }
public FullName Path { get; }
public AbsolutePathType Type { get; }
public AbsolutePath(IContentProvider contentProvider, FullName path, AbsolutePathType type, IContentProvider? virtualContentProvider = null)
{
ContentProvider = contentProvider;
Path = path;
VirtualContentProvider = virtualContentProvider;
Type = type;
}
public AbsolutePath(IItem item, IContentProvider? virtualContentProvider = null)
{
ContentProvider = item.Provider;
Path = item.FullName ?? throw new ArgumentException($"{nameof(item.FullName)} can not be null.", nameof(item));
VirtualContentProvider = virtualContentProvider;
Type = item.Type;
}
public async Task<IItem> ResolveAsync(bool forceResolve = false, ItemInitializationSettings itemInitializationSettings = default)
{
var provider = VirtualContentProvider ?? ContentProvider;
return await provider.GetItemByFullNameAsync(Path, forceResolve, Type, itemInitializationSettings);
}
public async Task<IItem?> ResolveAsyncSafe(bool forceResolve = false, ItemInitializationSettings itemInitializationSettings = default)
{
try
{
return await ResolveAsync(forceResolve, itemInitializationSettings);
}
catch { return null; }
}
}

View File

@@ -4,6 +4,7 @@ using System.Reactive.Subjects;
using DynamicData; using DynamicData;
using FileTime.Core.Enums; using FileTime.Core.Enums;
using FileTime.Core.Services; using FileTime.Core.Services;
using FileTime.Core.Timeline;
namespace FileTime.Core.Models; namespace FileTime.Core.Models;
@@ -12,7 +13,7 @@ public record Container(
string DisplayName, string DisplayName,
FullName FullName, FullName FullName,
NativePath NativePath, NativePath NativePath,
IAbsolutePath? Parent, AbsolutePath? Parent,
bool IsHidden, bool IsHidden,
bool IsExists, bool IsExists,
DateTime? CreatedAt, DateTime? CreatedAt,
@@ -20,9 +21,10 @@ public record Container(
bool CanRename, bool CanRename,
string? Attributes, string? Attributes,
IContentProvider Provider, IContentProvider Provider,
PointInTime PointInTime,
IObservable<IEnumerable<Exception>> Exceptions, IObservable<IEnumerable<Exception>> Exceptions,
ReadOnlyExtensionCollection Extensions, ReadOnlyExtensionCollection Extensions,
IObservable<IObservable<IChangeSet<IAbsolutePath>>?> Items) : IContainer IObservable<IObservable<IChangeSet<AbsolutePath>>?> Items) : IContainer
{ {
BehaviorSubject<bool> IsLoading { get; } = new BehaviorSubject<bool>(false); BehaviorSubject<bool> IsLoading { get; } = new BehaviorSubject<bool>(false);
IObservable<bool> IContainer.IsLoading => IsLoading.AsObservable(); IObservable<bool> IContainer.IsLoading => IsLoading.AsObservable();

View File

@@ -1,6 +1,7 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using FileTime.Core.Enums; using FileTime.Core.Enums;
using FileTime.Core.Services; using FileTime.Core.Services;
using FileTime.Core.Timeline;
namespace FileTime.Core.Models; namespace FileTime.Core.Models;
@@ -9,7 +10,7 @@ public record Element(
string DisplayName, string DisplayName,
FullName FullName, FullName FullName,
NativePath NativePath, NativePath NativePath,
IAbsolutePath? Parent, AbsolutePath? Parent,
bool IsHidden, bool IsHidden,
bool IsExists, bool IsExists,
DateTime? CreatedAt, DateTime? CreatedAt,
@@ -17,6 +18,7 @@ public record Element(
bool CanRename, bool CanRename,
string? Attributes, string? Attributes,
IContentProvider Provider, IContentProvider Provider,
PointInTime PointInTime,
IObservable<IEnumerable<Exception>> Exceptions, IObservable<IEnumerable<Exception>> Exceptions,
ReadOnlyExtensionCollection Extensions) : IElement ReadOnlyExtensionCollection Extensions) : IElement
{ {

View File

@@ -4,6 +4,7 @@ using System.Reactive.Subjects;
using DynamicData; using DynamicData;
using FileTime.Core.Enums; using FileTime.Core.Enums;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Timeline;
namespace FileTime.Core.Services; namespace FileTime.Core.Services;
@@ -11,10 +12,10 @@ public abstract class ContentProviderBase : IContentProvider
{ {
private readonly ReadOnlyExtensionCollection _extensions; private readonly ReadOnlyExtensionCollection _extensions;
protected BehaviorSubject<IObservable<IChangeSet<IAbsolutePath>>?> Items { get; } = new(null); protected BehaviorSubject<IObservable<IChangeSet<AbsolutePath>>?> Items { get; } = new(null);
protected ExtensionCollection Extensions { get; } protected ExtensionCollection Extensions { get; }
IObservable<IObservable<IChangeSet<IAbsolutePath>>?> IContainer.Items => Items; IObservable<IObservable<IChangeSet<AbsolutePath>>?> IContainer.Items => Items;
public string Name { get; } public string Name { get; }
@@ -34,7 +35,7 @@ public abstract class ContentProviderBase : IContentProvider
public IContentProvider Provider => this; public IContentProvider Provider => this;
public IAbsolutePath? Parent => null; public AbsolutePath? Parent => null;
public DateTime? CreatedAt => null; public DateTime? CreatedAt => null;
@@ -45,6 +46,7 @@ public abstract class ContentProviderBase : IContentProvider
IObservable<bool> IContainer.IsLoading => IsLoading.AsObservable(); IObservable<bool> IContainer.IsLoading => IsLoading.AsObservable();
public AbsolutePathType Type => AbsolutePathType.Container; public AbsolutePathType Type => AbsolutePathType.Container;
public PointInTime PointInTime { get; } = PointInTime.Eternal;
public IObservable<IEnumerable<Exception>> Exceptions => Observable.Return(Enumerable.Empty<Exception>()); public IObservable<IEnumerable<Exception>> Exceptions => Observable.Return(Enumerable.Empty<Exception>());
@@ -60,21 +62,21 @@ public abstract class ContentProviderBase : IContentProvider
public virtual Task OnEnter() => Task.CompletedTask; public virtual Task OnEnter() => Task.CompletedTask;
public virtual async Task<IItem> GetItemByFullNameAsync( public virtual async Task<IItem> GetItemByFullNameAsync(FullName fullName,
FullName fullName, PointInTime pointInTime,
bool forceResolve = false, bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown, AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default) ItemInitializationSettings itemInitializationSettings = default)
=> await GetItemByNativePathAsync(GetNativePath(fullName), forceResolve, forceResolvePathType, => await GetItemByNativePathAsync(GetNativePath(fullName), pointInTime, forceResolve, forceResolvePathType,
itemInitializationSettings); itemInitializationSettings);
public abstract Task<IItem> GetItemByNativePathAsync( public abstract Task<IItem> GetItemByNativePathAsync(NativePath nativePath,
NativePath nativePath, PointInTime pointInTime,
bool forceResolve = false, bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown, AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default); ItemInitializationSettings itemInitializationSettings = default);
public abstract Task<List<IAbsolutePath>> GetItemsByContainerAsync(FullName fullName); public abstract Task<List<AbsolutePath>> GetItemsByContainerAsync(FullName fullName, PointInTime pointInTime);
public abstract NativePath GetNativePath(FullName fullName); public abstract NativePath GetNativePath(FullName fullName);
public abstract Task<byte[]?> GetContentAsync(IElement element, public abstract Task<byte[]?> GetContentAsync(IElement element,

View File

@@ -3,22 +3,30 @@ using System.Reactive.Subjects;
using DynamicData; using DynamicData;
using DynamicData.Alias; using DynamicData.Alias;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Timeline;
namespace FileTime.Core.Services; namespace FileTime.Core.Services;
public class Tab : ITab public class Tab : ITab
{ {
private readonly ITimelessContentProvider _timelessContentProvider;
private readonly BehaviorSubject<IContainer?> _currentLocation = new(null); private readonly BehaviorSubject<IContainer?> _currentLocation = new(null);
private readonly BehaviorSubject<IAbsolutePath?> _currentSelectedItem = new(null); private readonly BehaviorSubject<AbsolutePath?> _currentSelectedItem = new(null);
private readonly SourceList<ItemFilter> _itemFilters = new(); private readonly SourceList<ItemFilter> _itemFilters = new();
private IAbsolutePath? _currentSelectedItemCached; private AbsolutePath? _currentSelectedItemCached;
private PointInTime _currentPointInTime;
public IObservable<IContainer?> CurrentLocation { get; } public IObservable<IContainer?> CurrentLocation { get; }
public IObservable<IObservable<IChangeSet<IItem>>?> CurrentItems { get; } public IObservable<IObservable<IChangeSet<IItem>>?> CurrentItems { get; }
public IObservable<IAbsolutePath?> CurrentSelectedItem { get; } public IObservable<AbsolutePath?> CurrentSelectedItem { get; }
public Tab() public Tab(ITimelessContentProvider timelessContentProvider)
{ {
_timelessContentProvider = timelessContentProvider;
_currentPointInTime = null!;
_timelessContentProvider.CurrentPointInTime.Subscribe(p => _currentPointInTime = p);
CurrentLocation = _currentLocation.DistinctUntilChanged().Publish(null).RefCount(); CurrentLocation = _currentLocation.DistinctUntilChanged().Publish(null).RefCount();
CurrentItems = CurrentItems =
Observable.Merge( Observable.Merge(
@@ -66,22 +74,22 @@ public class Tab : ITab
}); });
} }
private async Task<IItem> MapItem(IAbsolutePath item) => await item.ResolveAsync(true); private async Task<IItem> MapItem(AbsolutePath item) => await item.ResolveAsync(true);
public void Init(IContainer currentLocation) public void Init(IContainer currentLocation)
{ {
_currentLocation.OnNext(currentLocation); _currentLocation.OnNext(currentLocation);
} }
private static IAbsolutePath? GetSelectedItemByItems(IEnumerable<IItem> items) private AbsolutePath? GetSelectedItemByItems(IEnumerable<IItem> items)
{ {
//TODO: //TODO:
return new AbsolutePath(items.First()); return new AbsolutePath(_timelessContentProvider, items.First());
} }
public void SetCurrentLocation(IContainer newLocation) => _currentLocation.OnNext(newLocation); public void SetCurrentLocation(IContainer newLocation) => _currentLocation.OnNext(newLocation);
public void SetSelectedItem(IAbsolutePath newSelectedItem) => _currentSelectedItem.OnNext(newSelectedItem); public void SetSelectedItem(AbsolutePath newSelectedItem) => _currentSelectedItem.OnNext(newSelectedItem);
public void AddItemFilter(ItemFilter filter) => _itemFilters.Add(filter); public void AddItemFilter(ItemFilter filter) => _itemFilters.Add(filter);
public void RemoveItemFilter(ItemFilter filter) => _itemFilters.Remove(filter); public void RemoveItemFilter(ItemFilter filter) => _itemFilters.Remove(filter);
@@ -95,7 +103,8 @@ public class Tab : ITab
public async Task OpenSelected() public async Task OpenSelected()
{ {
if (_currentSelectedItemCached == null) return; if (_currentSelectedItemCached == null) return;
var resolvedSelectedItem = await _currentSelectedItemCached.ContentProvider.GetItemByFullNameAsync(_currentSelectedItemCached.Path); var resolvedSelectedItem =
await _currentSelectedItemCached.TimelessProvider.GetItemByFullNameAsync(_currentSelectedItemCached.Path, _currentPointInTime);
if (resolvedSelectedItem is not IContainer resolvedContainer) return; if (resolvedSelectedItem is not IContainer resolvedContainer) return;
SetCurrentLocation(resolvedContainer); SetCurrentLocation(resolvedContainer);

View File

@@ -0,0 +1,6 @@
namespace FileTime.Core.Timeline;
public class CommandScheduler : ICommandScheduler
{
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,38 @@
using System.Reactive.Subjects;
using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Services;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.Core.Timeline;
public class TimelessContentProvider : ITimelessContentProvider
{
private readonly Lazy<List<IContentProvider>> _contentProviders;
public BehaviorSubject<PointInTime> CurrentPointInTime { get; } =
new BehaviorSubject<PointInTime>(PointInTime.Present);
public TimelessContentProvider(IServiceProvider serviceProvider)
{
_contentProviders =
new Lazy<List<IContentProvider>>(() => serviceProvider.GetServices<IContentProvider>().ToList());
}
public async Task<IItem> GetItemByFullNameAsync(FullName fullName, PointInTime? pointInTime,
bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default)
{
//TODO time modifications
var contentProviderName = fullName.Path.Split(Constants.SeparatorChar).FirstOrDefault();
var contentProvider = _contentProviders.Value.FirstOrDefault(p => p.Name == contentProviderName);
if (contentProvider is null)
throw new Exception($"No content provider is found for name '{contentProviderName}'");
return await contentProvider.GetItemByFullNameAsync(fullName, pointInTime ?? PointInTime.Present,
forceResolve, forceResolvePathType,
itemInitializationSettings);
}
}

View File

@@ -53,6 +53,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Tools", "Tools\Fil
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Core.Command", "Core\FileTime.Core.Command\FileTime.Core.Command.csproj", "{1846BE76-8F68-4FC3-8954-871F14743F0B}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Core.Command", "Core\FileTime.Core.Command\FileTime.Core.Command.csproj", "{1846BE76-8F68-4FC3-8954-871F14743F0B}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Core.Timeline", "Core\FileTime.Core.Timeline\FileTime.Core.Timeline.csproj", "{2AC5CAFF-EBDA-4C6E-BCFF-304B651F2906}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -127,6 +129,10 @@ Global
{1846BE76-8F68-4FC3-8954-871F14743F0B}.Debug|Any CPU.Build.0 = Debug|Any CPU {1846BE76-8F68-4FC3-8954-871F14743F0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1846BE76-8F68-4FC3-8954-871F14743F0B}.Release|Any CPU.ActiveCfg = Release|Any CPU {1846BE76-8F68-4FC3-8954-871F14743F0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1846BE76-8F68-4FC3-8954-871F14743F0B}.Release|Any CPU.Build.0 = Release|Any CPU {1846BE76-8F68-4FC3-8954-871F14743F0B}.Release|Any CPU.Build.0 = Release|Any CPU
{2AC5CAFF-EBDA-4C6E-BCFF-304B651F2906}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2AC5CAFF-EBDA-4C6E-BCFF-304B651F2906}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2AC5CAFF-EBDA-4C6E-BCFF-304B651F2906}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2AC5CAFF-EBDA-4C6E-BCFF-304B651F2906}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -150,6 +156,7 @@ Global
{D7D1C76A-05B0-49BC-BCFF-06340E264EC1} = {01F231DE-4A65-435F-B4BB-77EE5221890C} {D7D1C76A-05B0-49BC-BCFF-06340E264EC1} = {01F231DE-4A65-435F-B4BB-77EE5221890C}
{B7A45654-E56C-43C8-998E-0F4661395540} = {8C3CFEFE-78A5-4940-B388-D15FCE02ECE9} {B7A45654-E56C-43C8-998E-0F4661395540} = {8C3CFEFE-78A5-4940-B388-D15FCE02ECE9}
{1846BE76-8F68-4FC3-8954-871F14743F0B} = {3324D046-1E05-46B5-B1BA-82910D56B332} {1846BE76-8F68-4FC3-8954-871F14743F0B} = {3324D046-1E05-46B5-B1BA-82910D56B332}
{2AC5CAFF-EBDA-4C6E-BCFF-304B651F2906} = {3324D046-1E05-46B5-B1BA-82910D56B332}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {859FB3DF-C60A-46B1-82E5-90274905D1EF} SolutionGuid = {859FB3DF-C60A-46B1-82E5-90274905D1EF}

View File

@@ -2,7 +2,7 @@ using FileTime.Core.Models;
namespace FileTime.GuiApp.Models; namespace FileTime.GuiApp.Models;
public interface IHaveAbsolutePath public interface IHaveFullPath
{ {
IAbsolutePath Path { get; } FullName Path { get; }
} }

View File

@@ -7,7 +7,7 @@ using IContainer = FileTime.Core.Models.IContainer;
namespace FileTime.GuiApp.ViewModels; namespace FileTime.GuiApp.ViewModels;
public partial class RootDriveInfo : IHaveAbsolutePath, INotifyPropertyChanged public partial class RootDriveInfo : IHaveFullPath, INotifyPropertyChanged
{ {
private readonly DriveInfo _driveInfo; private readonly DriveInfo _driveInfo;
@@ -25,7 +25,7 @@ public partial class RootDriveInfo : IHaveAbsolutePath, INotifyPropertyChanged
[Notify] public long UsedPercentage => Size == 0 ? 0 : Used * 100 / Size; [Notify] public long UsedPercentage => Size == 0 ? 0 : Used * 100 / Size;
public IAbsolutePath Path { get; } public FullName Path { get; }
public RootDriveInfo(DriveInfo driveInfo, IContainer container) public RootDriveInfo(DriveInfo driveInfo, IContainer container)
{ {
@@ -42,7 +42,7 @@ public partial class RootDriveInfo : IHaveAbsolutePath, INotifyPropertyChanged
{ {
} }
Path = new AbsolutePath(container); Path = container.FullName ?? throw new NullReferenceException($"Container does not have a {nameof(FullName)}");
Refresh(); Refresh();
} }

View File

@@ -5,6 +5,7 @@ using DynamicData.Binding;
using FileTime.App.Core.Models; using FileTime.App.Core.Models;
using FileTime.App.Core.Services; using FileTime.App.Core.Services;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Timeline;
using FileTime.GuiApp.ViewModels; using FileTime.GuiApp.ViewModels;
using FileTime.Providers.Local; using FileTime.Providers.Local;
using IContainer = FileTime.Core.Models.IContainer; using IContainer = FileTime.Core.Models.IContainer;
@@ -14,14 +15,14 @@ namespace FileTime.GuiApp.Services;
public class RootDriveInfoService : IStartupHandler public class RootDriveInfoService : IStartupHandler
{ {
private readonly SourceList<DriveInfo> _rootDrives = new(); private readonly SourceList<DriveInfo> _rootDrives = new();
private readonly IObservable<IChangeSet<IAbsolutePath>> _localContentProviderStream; private readonly IObservable<IChangeSet<AbsolutePath>> _localContentProviderStream;
public RootDriveInfoService(IGuiAppState guiAppState, ILocalContentProvider localContentProvider) public RootDriveInfoService(IGuiAppState guiAppState, ILocalContentProvider localContentProvider, ITimelessContentProvider timelessContentProvider)
{ {
InitRootDrives(); InitRootDrives();
var localContentProviderAsList = new SourceList<IAbsolutePath>(); var localContentProviderAsList = new SourceList<AbsolutePath>();
localContentProviderAsList.Add(new AbsolutePath(localContentProvider)); localContentProviderAsList.Add(new AbsolutePath(timelessContentProvider, localContentProvider));
_localContentProviderStream = localContentProviderAsList.Connect(); _localContentProviderStream = localContentProviderAsList.Connect();
var rootDriveInfos = Observable.CombineLatest( var rootDriveInfos = Observable.CombineLatest(
@@ -30,7 +31,7 @@ public class RootDriveInfoService : IStartupHandler
(items, drives) => (items, drives) =>
{ {
return items is null return items is null
? Observable.Empty<IChangeSet<(IAbsolutePath Path, DriveInfo? Drive)>>() ? Observable.Empty<IChangeSet<(AbsolutePath Path, DriveInfo? Drive)>>()
: items! : items!
.Or(new[] { _localContentProviderStream }) .Or(new[] { _localContentProviderStream })
.Transform(i => (Path: i, Drive: drives.FirstOrDefault(d => .Transform(i => (Path: i, Drive: drives.FirstOrDefault(d =>

View File

@@ -1,9 +1,11 @@
using System.Reflection; using System.Reflection;
using Avalonia.Input; using Avalonia.Input;
using FileTime.App.Core.Services; using FileTime.App.Core.Services;
using FileTime.App.Core.UserCommand;
using FileTime.App.Core.ViewModels; using FileTime.App.Core.ViewModels;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Services; using FileTime.Core.Services;
using FileTime.Core.Timeline;
using FileTime.GuiApp.Services; using FileTime.GuiApp.Services;
using FileTime.Providers.Local; using FileTime.Providers.Local;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@@ -22,6 +24,7 @@ namespace FileTime.GuiApp.ViewModels;
[Inject(typeof(LifecycleService), PropertyName = "_lifecycleService")] [Inject(typeof(LifecycleService), PropertyName = "_lifecycleService")]
[Inject(typeof(IItemPreviewService), PropertyAccessModifier = AccessModifier.Public)] [Inject(typeof(IItemPreviewService), PropertyAccessModifier = AccessModifier.Public)]
[Inject(typeof(IDialogService), PropertyAccessModifier = AccessModifier.Public)] [Inject(typeof(IDialogService), PropertyAccessModifier = AccessModifier.Public)]
[Inject(typeof(ITimelessContentProvider), PropertyName = "_timelessContentProvider")]
public partial class MainWindowViewModel : IMainWindowViewModelBase public partial class MainWindowViewModel : IMainWindowViewModelBase
{ {
public bool Loading => false; public bool Loading => false;
@@ -42,12 +45,14 @@ public partial class MainWindowViewModel : IMainWindowViewModelBase
versionString += $" ({version.Revision})"; versionString += $" ({version.Revision})";
} }
} }
Title = "FileTime " + versionString; Title = "FileTime " + versionString;
//TODO: refactor //TODO: refactor
if (AppState.Tabs.Count == 0) if (AppState.Tabs.Count == 0)
{ {
var tab = _serviceProvider.GetInitableResolver<IContainer>(_localContentProvider).GetRequiredService<ITab>(); var tab = _serviceProvider.GetInitableResolver<IContainer>(_localContentProvider)
.GetRequiredService<ITab>();
var tabViewModel = _serviceProvider.GetInitableResolver(tab, 1).GetRequiredService<ITabViewModel>(); var tabViewModel = _serviceProvider.GetInitableResolver(tab, 1).GetRequiredService<ITabViewModel>();
_appState.AddTab(tabViewModel); _appState.AddTab(tabViewModel);
@@ -58,4 +63,12 @@ public partial class MainWindowViewModel : IMainWindowViewModelBase
{ {
_keyInputHandlerService.ProcessKeyDown(key, keyModifiers, setHandled); _keyInputHandlerService.ProcessKeyDown(key, keyModifiers, setHandled);
} }
public async Task OpenContainerByFullName(FullName fullName)
{
var resolvedItem = await _timelessContentProvider.GetItemByFullNameAsync(fullName, PointInTime.Present);
if (resolvedItem is not IContainer resolvedContainer) return;
await UserCommandHandlerService.HandleCommandAsync(
new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, resolvedContainer)));
}
} }

View File

@@ -74,12 +74,12 @@ public partial class MainWindow : Window
&& e.GetCurrentPoint(this).Properties.IsLeftButtonPressed && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed
&& sender is StyledElement control) && sender is StyledElement control)
{ {
IAbsolutePath? path = null; FullName? path = null;
if (control.DataContext is IHaveAbsolutePath { Path: { } } haveAbsolutePath) if (control.DataContext is IHaveFullPath { Path: { } } hasFullPath)
{ {
path = haveAbsolutePath.Path; path = hasFullPath.Path;
} }
else if (control.DataContext is IAbsolutePath p) else if (control.DataContext is FullName p)
{ {
path = p; path = p;
} }
@@ -97,10 +97,7 @@ public partial class MainWindow : Window
if (path is null) return; if (path is null) return;
var resolvedItem = await path.ResolveAsync(); await ViewModel.OpenContainerByFullName(path);
if (resolvedItem is not IContainer resolvedContainer) return;
await ViewModel.UserCommandHandlerService.HandleCommandAsync(
new OpenContainerCommand(new AbsolutePath(resolvedContainer)));
e.Handled = true; e.Handled = true;
} }
} }

View File

@@ -6,16 +6,19 @@ using FileTime.App.Core.Models;
using FileTime.Core.Enums; using FileTime.Core.Enums;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Services; using FileTime.Core.Services;
using FileTime.Core.Timeline;
namespace FileTime.Providers.Local; namespace FileTime.Providers.Local;
public sealed partial class LocalContentProvider : ContentProviderBase, ILocalContentProvider public sealed partial class LocalContentProvider : ContentProviderBase, ILocalContentProvider
{ {
private readonly SourceList<IAbsolutePath> _rootDirectories = new(); private readonly ITimelessContentProvider _timelessContentProvider;
private readonly SourceList<AbsolutePath> _rootDirectories = new();
private readonly bool _isCaseInsensitive; private readonly bool _isCaseInsensitive;
public LocalContentProvider() : base("local") public LocalContentProvider(ITimelessContentProvider timelessContentProvider) : base("local")
{ {
_timelessContentProvider = timelessContentProvider;
_isCaseInsensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); _isCaseInsensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
RefreshRootDirectories(); RefreshRootDirectories();
@@ -39,12 +42,12 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
_rootDirectories.Edit(actions => _rootDirectories.Edit(actions =>
{ {
actions.Clear(); actions.Clear();
actions.AddRange(rootDirectories.Select(DirectoryToAbsolutePath)); actions.AddRange(rootDirectories.Select(d => DirectoryToAbsolutePath(d, PointInTime.Present)));
}); });
} }
public override Task<IItem> GetItemByNativePathAsync( public override Task<IItem> GetItemByNativePathAsync(NativePath nativePath,
NativePath nativePath, PointInTime pointInTime,
bool forceResolve = false, bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown, AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default) ItemInitializationSettings itemInitializationSettings = default)
@@ -61,12 +64,13 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
{ {
return Task.FromResult((IItem)DirectoryToContainer( return Task.FromResult((IItem)DirectoryToContainer(
new DirectoryInfo(path!.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar), new DirectoryInfo(path!.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar),
pointInTime,
!itemInitializationSettings.SkipChildInitialization) !itemInitializationSettings.SkipChildInitialization)
); );
} }
else if (File.Exists(path)) else if (File.Exists(path))
{ {
return Task.FromResult((IItem)FileToElement(new FileInfo(path))); return Task.FromResult((IItem)FileToElement(new FileInfo(path), pointInTime));
} }
var type = forceResolvePathType switch var type = forceResolvePathType switch
@@ -98,7 +102,11 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
return forceResolvePathType switch return forceResolvePathType switch
{ {
AbsolutePathType.Container => Task.FromResult( AbsolutePathType.Container => Task.FromResult(
(IItem)CreateEmptyContainer(nativePath, Observable.Return(new List<Exception>() { innerException })) (IItem)CreateEmptyContainer(
nativePath,
pointInTime,
Observable.Return(new List<Exception>() { innerException })
)
), ),
AbsolutePathType.Element => Task.FromResult(CreateEmptyElement(nativePath)), AbsolutePathType.Element => Task.FromResult(CreateEmptyElement(nativePath)),
_ => throw new Exception( _ => throw new Exception(
@@ -108,6 +116,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
} }
private Container CreateEmptyContainer(NativePath nativePath, private Container CreateEmptyContainer(NativePath nativePath,
PointInTime pointInTime,
IObservable<IEnumerable<Exception>>? exceptions = null) IObservable<IEnumerable<Exception>>? exceptions = null)
{ {
var nonNullExceptions = exceptions ?? Observable.Return(Enumerable.Empty<Exception>()); var nonNullExceptions = exceptions ?? Observable.Return(Enumerable.Empty<Exception>());
@@ -116,7 +125,8 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
var parentFullName = fullName.GetParent(); var parentFullName = fullName.GetParent();
var parent = new AbsolutePath( var parent = new AbsolutePath(
this, _timelessContentProvider,
pointInTime,
parentFullName ?? new FullName(""), parentFullName ?? new FullName(""),
AbsolutePathType.Container); AbsolutePathType.Container);
@@ -133,9 +143,10 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
false, false,
"???", "???",
this, this,
pointInTime,
nonNullExceptions, nonNullExceptions,
new ExtensionCollection().AsReadOnly(), new ExtensionCollection().AsReadOnly(),
Observable.Return<IObservable<IChangeSet<IAbsolutePath>>?>(null) Observable.Return<IObservable<IChangeSet<AbsolutePath>>?>(null)
); );
} }
@@ -144,41 +155,43 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override Task<List<IAbsolutePath>> GetItemsByContainerAsync(FullName fullName) public override Task<List<AbsolutePath>> GetItemsByContainerAsync(FullName fullName, PointInTime pointInTime)
=> Task.FromResult(GetItemsByContainer(fullName)); => Task.FromResult(GetItemsByContainer(fullName, pointInTime));
private List<IAbsolutePath> GetItemsByContainer(FullName fullName) private List<AbsolutePath> GetItemsByContainer(FullName fullName, PointInTime pointInTime)
=> GetItemsByContainer(new DirectoryInfo(GetNativePath(fullName).Path)); => GetItemsByContainer(new DirectoryInfo(GetNativePath(fullName).Path), pointInTime);
private List<IAbsolutePath> GetItemsByContainer(DirectoryInfo directoryInfo) private List<AbsolutePath> GetItemsByContainer(DirectoryInfo directoryInfo, PointInTime pointInTime)
=> directoryInfo => directoryInfo
.GetDirectories() .GetDirectories()
.Select(DirectoryToAbsolutePath) .Select(d => DirectoryToAbsolutePath(d, pointInTime))
.Concat( .Concat(
directoryInfo directoryInfo
.GetFiles() .GetFiles()
.Select(FileToAbsolutePath) .Select(f => FileToAbsolutePath(f, pointInTime))
) )
.ToList(); .ToList();
private IAbsolutePath DirectoryToAbsolutePath(DirectoryInfo directoryInfo) private AbsolutePath DirectoryToAbsolutePath(DirectoryInfo directoryInfo, PointInTime pointInTime)
{ {
var fullName = GetFullName(directoryInfo); var fullName = GetFullName(directoryInfo);
return new AbsolutePath(this, fullName, AbsolutePathType.Container); return new AbsolutePath(_timelessContentProvider, pointInTime, fullName, AbsolutePathType.Container);
} }
private IAbsolutePath FileToAbsolutePath(FileInfo file) private AbsolutePath FileToAbsolutePath(FileInfo file, PointInTime pointInTime)
{ {
var fullName = GetFullName(file); var fullName = GetFullName(file);
return new AbsolutePath(this, fullName, AbsolutePathType.Element); return new AbsolutePath(_timelessContentProvider, pointInTime, fullName, AbsolutePathType.Element);
} }
private Container DirectoryToContainer(DirectoryInfo directoryInfo, bool initializeChildren = true) private Container DirectoryToContainer(DirectoryInfo directoryInfo, PointInTime pointInTime,
bool initializeChildren = true)
{ {
var fullName = GetFullName(directoryInfo.FullName); var fullName = GetFullName(directoryInfo.FullName);
var parentFullName = fullName.GetParent(); var parentFullName = fullName.GetParent();
var parent = new AbsolutePath( var parent = new AbsolutePath(
this, _timelessContentProvider,
pointInTime,
parentFullName ?? new FullName(""), parentFullName ?? new FullName(""),
AbsolutePathType.Container); AbsolutePathType.Container);
var exceptions = new BehaviorSubject<IEnumerable<Exception>>(Enumerable.Empty<Exception>()); var exceptions = new BehaviorSubject<IEnumerable<Exception>>(Enumerable.Empty<Exception>());
@@ -196,20 +209,21 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
true, true,
GetDirectoryAttributes(directoryInfo), GetDirectoryAttributes(directoryInfo),
this, this,
pointInTime,
exceptions, exceptions,
new ExtensionCollection().AsReadOnly(), new ExtensionCollection().AsReadOnly(),
Observable.FromAsync(async () => await Task.Run(InitChildren)) Observable.FromAsync(async () => await Task.Run(InitChildren))
); );
Task<IObservable<IChangeSet<IAbsolutePath>>?> InitChildren() Task<IObservable<IChangeSet<AbsolutePath>>?> InitChildren()
{ {
SourceList<IAbsolutePath>? result = null; SourceList<AbsolutePath>? result = null;
try try
{ {
var items = initializeChildren ? (List<IAbsolutePath>?)GetItemsByContainer(directoryInfo) : null; var items = initializeChildren ? (List<AbsolutePath>?)GetItemsByContainer(directoryInfo, pointInTime) : null;
if (items != null) if (items != null)
{ {
result = new SourceList<IAbsolutePath>(); result = new SourceList<AbsolutePath>();
result.AddRange(items); result.AddRange(items);
} }
} }
@@ -222,12 +236,13 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
} }
} }
private Element FileToElement(FileInfo fileInfo) private Element FileToElement(FileInfo fileInfo, PointInTime pointInTime)
{ {
var fullName = GetFullName(fileInfo); var fullName = GetFullName(fileInfo);
var parentFullName = fullName.GetParent() ?? var parentFullName = fullName.GetParent() ??
throw new Exception($"Path does not have parent: '{fileInfo.FullName}'"); throw new Exception($"Path does not have parent: '{fileInfo.FullName}'");
var parent = new AbsolutePath(this, parentFullName, AbsolutePathType.Container); var parent = new AbsolutePath(_timelessContentProvider, pointInTime, parentFullName,
AbsolutePathType.Container);
var extensions = new ExtensionCollection() var extensions = new ExtensionCollection()
{ {
@@ -247,6 +262,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
true, true,
GetFileAttributes(fileInfo), GetFileAttributes(fileInfo),
this, this,
pointInTime,
Observable.Return(Enumerable.Empty<Exception>()), Observable.Return(Enumerable.Empty<Exception>()),
extensions.AsReadOnly() extensions.AsReadOnly()
); );
@@ -277,7 +293,8 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
if (!File.Exists(element.NativePath!.Path)) if (!File.Exists(element.NativePath!.Path))
throw new FileNotFoundException("File does not exist", element.NativePath.Path); throw new FileNotFoundException("File does not exist", element.NativePath.Path);
await using var reader = new FileStream(element.NativePath!.Path, FileMode.Open, FileAccess.Read, FileShare.Read, await using var reader = new FileStream(element.NativePath!.Path, FileMode.Open, FileAccess.Read,
FileShare.Read,
bufferSize: 1, // bufferSize == 1 used to avoid unnecessary buffer in FileStream bufferSize: 1, // bufferSize == 1 used to avoid unnecessary buffer in FileStream
FileOptions.Asynchronous | FileOptions.SequentialScan); FileOptions.Asynchronous | FileOptions.SequentialScan);