Contaienr size scan WIP
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>FileTime.App.ContainerSizeScanner</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Core\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
|
||||
<ProjectReference Include="..\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,9 @@
|
||||
using FileTime.Core.ContentAccess;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public interface IContainerScanSnapshotProvider : IContentProvider
|
||||
{
|
||||
ISizeScanTask StartSizeScan(IContainer scanSizeOf);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using DeclarativeProperty;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public interface IContainerSizeScanContainer : ISizeItem, IContainer
|
||||
{
|
||||
public Task AddSizeSourceAsync(IDeclarativeProperty<long> sizeElement);
|
||||
ObservableCollection<IContainerSizeScanContainer> ChildContainers { get; }
|
||||
IContainer RealContainer { get; init; }
|
||||
IDeclarativeProperty<long> Size { get; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public interface ISizeItem : IItem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public interface ISizePreviewItem : IItemPreviewViewModel
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using FileTime.Core.Models;
|
||||
using InitableService;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public interface ISizeScanTask : IInitable<IContainer>
|
||||
{
|
||||
IContainerSizeScanContainer SizeContainer { get; }
|
||||
void Start();
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
using FileTime.App.Core.Exceptions;
|
||||
using FileTime.Core.ContentAccess;
|
||||
using FileTime.Core.Enums;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Timeline;
|
||||
using InitableService;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public class ContainerScanSnapshotProvider : ContentProviderBase, IContainerScanSnapshotProvider
|
||||
{
|
||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly List<ISizeScanTask> _sizeScanTasks = new();
|
||||
internal const string ContentProviderName = "container-size-scan";
|
||||
|
||||
public ContainerScanSnapshotProvider(
|
||||
ITimelessContentProvider timelessContentProvider,
|
||||
IServiceProvider serviceProvider)
|
||||
: base(ContentProviderName, timelessContentProvider)
|
||||
{
|
||||
_timelessContentProvider = timelessContentProvider;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public override async Task<IItem> GetItemByFullNameAsync(
|
||||
FullName fullName,
|
||||
PointInTime pointInTime,
|
||||
bool forceResolve = false,
|
||||
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
||||
ItemInitializationSettings itemInitializationSettings = null
|
||||
)
|
||||
{
|
||||
if (fullName.Path == ContentProviderName)
|
||||
return this;
|
||||
|
||||
var pathParts = fullName.Path.Split(Constants.SeparatorChar);
|
||||
|
||||
var item = _sizeScanTasks.FirstOrDefault(t => t.SizeContainer.Name == pathParts[1])?.SizeContainer;
|
||||
|
||||
if (pathParts.Length == 2)
|
||||
return item ?? throw new ItemNotFoundException(fullName);
|
||||
|
||||
for (var i = 2; i < pathParts.Length - 1 && item != null; i++)
|
||||
{
|
||||
var childName = pathParts[i];
|
||||
item = item.ChildContainers.FirstOrDefault(c => c.Name == childName);
|
||||
}
|
||||
|
||||
if (item is not null)
|
||||
{
|
||||
var container = item.ChildContainers.FirstOrDefault(c => c.Name == pathParts[^1]);
|
||||
if (container is not null) return container;
|
||||
|
||||
var childName = item.RealContainer.FullName?.GetChild(pathParts[^1]);
|
||||
if (childName is null) throw new ItemNotFoundException(fullName);
|
||||
|
||||
return await _timelessContentProvider.GetItemByFullNameAsync(
|
||||
childName,
|
||||
pointInTime,
|
||||
forceResolve,
|
||||
forceResolvePathType,
|
||||
itemInitializationSettings
|
||||
);
|
||||
}
|
||||
|
||||
throw new ItemNotFoundException(fullName);
|
||||
}
|
||||
|
||||
public override async Task<IItem> GetItemByNativePathAsync(
|
||||
NativePath nativePath,
|
||||
PointInTime pointInTime,
|
||||
bool forceResolve = false,
|
||||
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
||||
ItemInitializationSettings itemInitializationSettings = default
|
||||
) =>
|
||||
await GetItemByFullNameAsync(
|
||||
new FullName(nativePath.Path),
|
||||
pointInTime,
|
||||
forceResolve,
|
||||
forceResolvePathType,
|
||||
itemInitializationSettings
|
||||
);
|
||||
|
||||
public override NativePath GetNativePath(FullName fullName)
|
||||
=> new(fullName.Path);
|
||||
|
||||
public override FullName GetFullName(NativePath nativePath)
|
||||
=> new(nativePath.Path);
|
||||
|
||||
public override Task<byte[]?> GetContentAsync(
|
||||
IElement element,
|
||||
int? maxLength = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
//TODO read from original source
|
||||
=> Task.FromResult((byte[]?) null);
|
||||
|
||||
public override bool CanHandlePath(NativePath path)
|
||||
=> path.Path.StartsWith(ContentProviderName);
|
||||
|
||||
public ISizeScanTask StartSizeScan(IContainer scanSizeOf)
|
||||
{
|
||||
var searchTask = _serviceProvider
|
||||
.GetInitableResolver(scanSizeOf)
|
||||
.GetRequiredService<ISizeScanTask>();
|
||||
|
||||
_sizeScanTasks.Add(searchTask);
|
||||
searchTask.Start();
|
||||
Items.Add(new AbsolutePath(_timelessContentProvider, searchTask.SizeContainer));
|
||||
|
||||
return searchTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||
using ObservableComputations;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public class ContainerSizeContainerPreview : ISizePreviewItem
|
||||
{
|
||||
public const string PreviewName = "SizePreviewContainer";
|
||||
public string Name => PreviewName;
|
||||
|
||||
public ObservableCollection<ISizePreviewItem> Items { get; }
|
||||
|
||||
public ContainerSizeContainerPreview(IContainerSizeScanContainer container)
|
||||
{
|
||||
Items = container
|
||||
.ChildContainers
|
||||
.Ordering(c => c.Size)
|
||||
.Taking(0, 10)
|
||||
.Selecting();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using FileTime.App.Core.Services;
|
||||
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public class ContainerSizePreviewProvider : IItemPreviewProvider
|
||||
{
|
||||
public bool CanHandle(IItem item) => item is ContainerSizeScanContainer;
|
||||
|
||||
public Task<IItemPreviewViewModel> CreatePreviewAsync(IItem item)
|
||||
{
|
||||
if(item is not ContainerSizeScanContainer container) throw new NotSupportedException();
|
||||
|
||||
return Task.FromResult((IItemPreviewViewModel)new ContainerSizeContainerPreview(container));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using DeclarativeProperty;
|
||||
using FileTime.Core.ContentAccess;
|
||||
using FileTime.Core.Enums;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Timeline;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
//TODO: create readonly version
|
||||
public class ContainerSizeScanContainer : IContainerSizeScanContainer
|
||||
{
|
||||
private readonly ReadOnlyExtensionCollection _readOnlyExtensions;
|
||||
private readonly BehaviorSubject<bool> _isLoading = new(false);
|
||||
private readonly CombineProperty<long, long> _size;
|
||||
public required string Name { get; init; }
|
||||
public required string DisplayName { get; init; }
|
||||
public required FullName? FullName { get; init; }
|
||||
public required NativePath? NativePath { get; init; }
|
||||
public AbsolutePath? Parent { get; init; }
|
||||
public bool IsHidden => false;
|
||||
public bool IsExists => true;
|
||||
public DateTime? CreatedAt { get; }
|
||||
public SupportsDelete CanDelete => SupportsDelete.True;
|
||||
public bool CanRename => false;
|
||||
public IContentProvider Provider { get; }
|
||||
public string? Attributes => null;
|
||||
public AbsolutePathType Type => AbsolutePathType.Container;
|
||||
public PointInTime PointInTime => PointInTime.Present;
|
||||
public ObservableCollection<Exception> Exceptions { get; } = new();
|
||||
public ExtensionCollection Extensions { get; } = new();
|
||||
ReadOnlyExtensionCollection IItem.Extensions => _readOnlyExtensions;
|
||||
public IItem WithParent(AbsolutePath parent) => throw new NotImplementedException();
|
||||
|
||||
public ObservableCollection<AbsolutePath> Items { get; } = new();
|
||||
public IObservable<bool> IsLoading { get; }
|
||||
public bool? IsLoaded { get; private set; }
|
||||
public Task WaitForLoaded(CancellationToken token = default) => throw new NotImplementedException();
|
||||
|
||||
public bool AllowRecursiveDeletion => false;
|
||||
|
||||
public IDeclarativeProperty<long> Size => _size;
|
||||
public ObservableCollection<IContainerSizeScanContainer> ChildContainers { get; } = new();
|
||||
public required IContainer RealContainer { get; init; }
|
||||
|
||||
public ContainerSizeScanContainer(IContainerScanSnapshotProvider provider)
|
||||
{
|
||||
_readOnlyExtensions = new ReadOnlyExtensionCollection(Extensions);
|
||||
IsLoading = _isLoading.AsObservable();
|
||||
|
||||
_size = new(childContainerSizes => Task.FromResult(childContainerSizes.Sum()));
|
||||
CreatedAt = DateTime.Now;
|
||||
Provider = provider;
|
||||
}
|
||||
|
||||
public async Task AddSizeSourceAsync(IDeclarativeProperty<long> sizeElement)
|
||||
=> await _size.AddSourceAsync(sizeElement);
|
||||
|
||||
public Task StartLoadingAsync()
|
||||
{
|
||||
_isLoading.OnNext(true);
|
||||
IsLoaded = false;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopLoadingAsync()
|
||||
{
|
||||
_isLoading.OnNext(false);
|
||||
IsLoaded = true;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using FileTime.Core.ContentAccess;
|
||||
using FileTime.Core.Enums;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Timeline;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public record ContainerSizeScanElement(
|
||||
string Name,
|
||||
string DisplayName,
|
||||
FullName FullName,
|
||||
NativePath NativePath,
|
||||
AbsolutePath? Parent,
|
||||
bool IsHidden,
|
||||
bool IsExists,
|
||||
DateTime? CreatedAt,
|
||||
SupportsDelete CanDelete,
|
||||
bool CanRename,
|
||||
string? Attributes,
|
||||
IContentProvider Provider,
|
||||
PointInTime PointInTime,
|
||||
ObservableCollection<Exception> Exceptions,
|
||||
ReadOnlyExtensionCollection Extensions) : IElement
|
||||
{
|
||||
public AbsolutePathType Type => AbsolutePathType.Element;
|
||||
|
||||
public IItem WithParent(AbsolutePath parent) => this with { Parent = parent };
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Core\FileTime.Core.ContentAccess\FileTime.Core.ContentAccess.csproj" />
|
||||
<ProjectReference Include="..\FileTime.App.ContainerSizeScanner.Abstractions\FileTime.App.ContainerSizeScanner.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
117
src/AppCommon/FileTime.App.ContainerSizeScanner/SizeScanTask.cs
Normal file
117
src/AppCommon/FileTime.App.ContainerSizeScanner/SizeScanTask.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using DeclarativeProperty;
|
||||
using FileTime.Core.Enums;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Models.Extensions;
|
||||
using FileTime.Core.Timeline;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public class SizeScanTask : ISizeScanTask
|
||||
{
|
||||
private IContainer _scanSizeOf = null!;
|
||||
private readonly IContainerScanSnapshotProvider _containerScanSnapshotProvider;
|
||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||
private readonly ILogger<SizeScanTask> _logger;
|
||||
private Thread? _sizeScanThread;
|
||||
private static int _searchId = 1;
|
||||
public IContainerSizeScanContainer SizeContainer { get; private set; } = null!;
|
||||
|
||||
public SizeScanTask(
|
||||
IContainerScanSnapshotProvider containerScanSnapshotProvider,
|
||||
ITimelessContentProvider timelessContentProvider,
|
||||
ILogger<SizeScanTask> logger)
|
||||
{
|
||||
_containerScanSnapshotProvider = containerScanSnapshotProvider;
|
||||
_timelessContentProvider = timelessContentProvider;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Init(IContainer scanSizeOf)
|
||||
{
|
||||
_scanSizeOf = scanSizeOf;
|
||||
var name = $"{_searchId++}_{scanSizeOf.Name}";
|
||||
var randomId = ContainerScanSnapshotProvider.ContentProviderName + Constants.SeparatorChar + name;
|
||||
SizeContainer = new ContainerSizeScanContainer(_containerScanSnapshotProvider)
|
||||
{
|
||||
Name = name,
|
||||
DisplayName = scanSizeOf.DisplayName,
|
||||
FullName = new FullName(randomId),
|
||||
NativePath = new NativePath(randomId),
|
||||
Parent = new AbsolutePath(_timelessContentProvider, _containerScanSnapshotProvider),
|
||||
RealContainer = scanSizeOf
|
||||
};
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (_sizeScanThread != null) return;
|
||||
|
||||
var sizeScanThread = new Thread(Run);
|
||||
sizeScanThread.Start();
|
||||
_sizeScanThread = sizeScanThread;
|
||||
}
|
||||
|
||||
private async void Run()
|
||||
{
|
||||
try
|
||||
{
|
||||
await TraverseTree(_scanSizeOf, SizeContainer);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error while scanning container {ContainerName}", _scanSizeOf.Name);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: make static
|
||||
private async Task TraverseTree(
|
||||
IContainer realContainer,
|
||||
IContainerSizeScanContainer container)
|
||||
{
|
||||
var resolvedItems = new List<IItem>(realContainer.Items.Count);
|
||||
foreach (var item in realContainer.Items)
|
||||
{
|
||||
var resolvedItem = await item.ResolveAsync();
|
||||
resolvedItems.Add(resolvedItem);
|
||||
}
|
||||
|
||||
foreach (var element in resolvedItems.OfType<IElement>())
|
||||
{
|
||||
var fileExtension = element.GetExtension<FileExtension>();
|
||||
if (fileExtension?.Size is not { } size) continue;
|
||||
|
||||
var childName = container.FullName!.GetChild(element.Name).Path;
|
||||
await container.AddSizeSourceAsync(new DeclarativeProperty<long>(size));
|
||||
container.Items.Add(new AbsolutePath(
|
||||
_timelessContentProvider,
|
||||
PointInTime.Present,
|
||||
new FullName(childName),
|
||||
AbsolutePathType.Element));
|
||||
}
|
||||
|
||||
foreach (var childContainer in resolvedItems.OfType<IContainer>())
|
||||
{
|
||||
var childName = container.FullName!.GetChild(childContainer.Name).Path;
|
||||
var childSearchContainer = new ContainerSizeScanContainer(_containerScanSnapshotProvider)
|
||||
{
|
||||
Name = childContainer.Name,
|
||||
DisplayName = childContainer.DisplayName,
|
||||
FullName = new FullName(childName),
|
||||
NativePath = new NativePath(childName),
|
||||
Parent = new AbsolutePath(_timelessContentProvider, container),
|
||||
RealContainer = childContainer
|
||||
};
|
||||
|
||||
container.ChildContainers.Add(childSearchContainer);
|
||||
await container.AddSizeSourceAsync(childSearchContainer.Size);
|
||||
container.Items.Add(new AbsolutePath(
|
||||
_timelessContentProvider,
|
||||
PointInTime.Present,
|
||||
new FullName(childName),
|
||||
AbsolutePathType.Container));
|
||||
|
||||
await TraverseTree(childContainer, childSearchContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/AppCommon/FileTime.App.ContainerSizeScanner/Startup.cs
Normal file
18
src/AppCommon/FileTime.App.ContainerSizeScanner/Startup.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using FileTime.App.Core.Services;
|
||||
using FileTime.Core.ContentAccess;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public static class Startup
|
||||
{
|
||||
public static IServiceCollection AddContainerSizeScanner(this IServiceCollection services)
|
||||
{
|
||||
services.TryAddSingleton<IContainerScanSnapshotProvider, ContainerScanSnapshotProvider>();
|
||||
services.AddSingleton<IContentProvider>(sp => sp.GetRequiredService<IContainerScanSnapshotProvider>());
|
||||
services.AddTransient<ISizeScanTask, SizeScanTask>();
|
||||
services.AddTransient<IItemPreviewProvider, ContainerSizePreviewProvider>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.App.Core.Services;
|
||||
|
||||
public interface IItemPreviewProvider
|
||||
{
|
||||
bool CanHandle(IItem item);
|
||||
Task<IItemPreviewViewModel> CreatePreviewAsync(IItem item);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace FileTime.App.Core.UserCommand;
|
||||
|
||||
public sealed class ScanSizeCommand : IIdentifiableUserCommand
|
||||
{
|
||||
public const string ScanSizeCommandName = "scan_size";
|
||||
public static readonly ScanSizeCommand Instance = new();
|
||||
|
||||
private ScanSizeCommand()
|
||||
{
|
||||
}
|
||||
|
||||
public string UserCommandID => ScanSizeCommandName;
|
||||
public string Title => "Scan size";
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using FileTime.App.Core.Models;
|
||||
|
||||
namespace FileTime.App.Core.ViewModels.ItemPreview;
|
||||
|
||||
public interface IElementPreviewViewModel : IItemPreviewViewModel
|
||||
{
|
||||
ItemPreviewMode Mode { get; }
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
using FileTime.App.Core.Models;
|
||||
|
||||
namespace FileTime.App.Core.ViewModels.ItemPreview;
|
||||
|
||||
public interface IItemPreviewViewModel
|
||||
{
|
||||
ItemPreviewMode Mode { get; }
|
||||
string Name { get; }
|
||||
}
|
||||
@@ -27,6 +27,7 @@
|
||||
<ProjectReference Include="..\..\Tools\FileTime.Tools\FileTime.Tools.csproj" />
|
||||
<ProjectReference Include="..\FileTime.App.CommandPalette.Abstractions\FileTime.App.CommandPalette.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\FileTime.App.CommandPalette\FileTime.App.CommandPalette.csproj" />
|
||||
<ProjectReference Include="..\FileTime.App.ContainerSizeScanner.Abstractions\FileTime.App.ContainerSizeScanner.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
|
||||
<ProjectReference Include="..\..\Core\FileTime.Core.Command\FileTime.Core.Command.csproj" />
|
||||
<ProjectReference Include="..\FileTime.App.FrequencyNavigation.Abstractions\FileTime.App.FrequencyNavigation.Abstractions.csproj" />
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||
using FileTime.Core.Models;
|
||||
using InitableService;
|
||||
|
||||
namespace FileTime.App.Core.Services;
|
||||
|
||||
public class ElementPreviewProvider : IItemPreviewProvider
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public ElementPreviewProvider(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public bool CanHandle(IItem item) => item is IElement;
|
||||
|
||||
public async Task<IItemPreviewViewModel> CreatePreviewAsync(IItem item)
|
||||
{
|
||||
if (item is not IElement element) throw new NotSupportedException();
|
||||
|
||||
return await _serviceProvider
|
||||
.GetAsyncInitableResolver(element)
|
||||
.GetRequiredServiceAsync<ElementPreviewViewModel>();
|
||||
}
|
||||
}
|
||||
@@ -9,14 +9,21 @@ namespace FileTime.App.Core.Services;
|
||||
public class ItemPreviewService : IItemPreviewService
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IEnumerable<IItemPreviewProvider> _itemPreviewProviders;
|
||||
public IObservable<IItemPreviewViewModel?> ItemPreview { get; }
|
||||
|
||||
public ItemPreviewService(IAppState appState, IServiceProvider serviceProvider)
|
||||
public ItemPreviewService(
|
||||
IAppState appState,
|
||||
IServiceProvider serviceProvider,
|
||||
IEnumerable<IItemPreviewProvider> itemPreviewProviders)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_itemPreviewProviders = itemPreviewProviders;
|
||||
ItemPreview = appState
|
||||
.SelectedTab
|
||||
.Select(t => t?.CurrentSelectedItem.Throttle(TimeSpan.FromMilliseconds(250)) ?? Observable.Return<IItemViewModel?>(null))
|
||||
.Select(t =>
|
||||
t?.CurrentSelectedItem.Throttle(TimeSpan.FromMilliseconds(250))
|
||||
?? Observable.Return<IItemViewModel?>(null))
|
||||
.Switch()
|
||||
.Select(item =>
|
||||
item == null
|
||||
@@ -30,11 +37,12 @@ public class ItemPreviewService : IItemPreviewService
|
||||
|
||||
private async Task<IItemPreviewViewModel?> Map(IItemViewModel itemViewModel)
|
||||
{
|
||||
return itemViewModel.BaseItem switch
|
||||
{
|
||||
IElement element => await _serviceProvider.GetAsyncInitableResolver(element)
|
||||
.GetRequiredServiceAsync<ElementPreviewViewModel>(),
|
||||
_ => null
|
||||
};
|
||||
ArgumentNullException.ThrowIfNull(itemViewModel.BaseItem);
|
||||
|
||||
var itemPreviewProvider = _itemPreviewProviders.FirstOrDefault(p => p.CanHandle(itemViewModel.BaseItem));
|
||||
|
||||
return itemPreviewProvider is null
|
||||
? null
|
||||
: await itemPreviewProvider.CreatePreviewAsync(itemViewModel.BaseItem);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,8 @@ public class TabPersistenceService : ITabPersistenceService
|
||||
//TODO: make this a configuration maybe?
|
||||
private readonly List<string> _contentProvidersNotToRestore = new()
|
||||
{
|
||||
"search"
|
||||
"search",
|
||||
"container-size-scan"
|
||||
};
|
||||
|
||||
private record PersistenceRoot(TabStates? TabStates);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Diagnostics;
|
||||
using DeclarativeProperty;
|
||||
using FileTime.App.ContainerSizeScanner;
|
||||
using FileTime.App.Core.UserCommand;
|
||||
using FileTime.App.Core.ViewModels;
|
||||
using FileTime.App.Search;
|
||||
using FileTime.Core.Command;
|
||||
using FileTime.Core.ContentAccess;
|
||||
using FileTime.Core.Enums;
|
||||
using FileTime.Core.Interactions;
|
||||
@@ -20,6 +22,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||
private readonly IUserCommandHandlerService _userCommandHandlerService;
|
||||
private readonly IContentAccessorFactory _contentAccessorFactory;
|
||||
private readonly IContainerScanSnapshotProvider _containerScanSnapshotProvider;
|
||||
private IDeclarativeProperty<IContainer?>? _currentLocation;
|
||||
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
|
||||
private ITabViewModel? _currentSelectedTab;
|
||||
@@ -32,7 +35,8 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
IItemNameConverterService itemNameConverterService,
|
||||
ITimelessContentProvider timelessContentProvider,
|
||||
IUserCommandHandlerService userCommandHandlerService,
|
||||
IContentAccessorFactory contentAccessorFactory) : base(appState)
|
||||
IContentAccessorFactory contentAccessorFactory,
|
||||
IContainerScanSnapshotProvider containerScanSnapshotProvider) : base(appState)
|
||||
{
|
||||
_systemClipboardService = systemClipboardService;
|
||||
_userCommunicationService = userCommunicationService;
|
||||
@@ -41,6 +45,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
_timelessContentProvider = timelessContentProvider;
|
||||
_userCommandHandlerService = userCommandHandlerService;
|
||||
_contentAccessorFactory = contentAccessorFactory;
|
||||
_containerScanSnapshotProvider = containerScanSnapshotProvider;
|
||||
SaveCurrentLocation(l => _currentLocation = l);
|
||||
SaveCurrentSelectedItem(i => _currentSelectedItem = i);
|
||||
SaveSelectedTab(t => _currentSelectedTab = t);
|
||||
@@ -51,10 +56,20 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
new TypeUserCommandHandler<CopyNativePathCommand>(CopyNativePath),
|
||||
new TypeUserCommandHandler<CopyBase64Command>(CopyBase64),
|
||||
new TypeUserCommandHandler<SearchCommand>(Search),
|
||||
new TypeUserCommandHandler<ScanSizeCommand>(ScanSize),
|
||||
new TypeUserCommandHandler<SortItemsCommand>(SortItems),
|
||||
});
|
||||
}
|
||||
|
||||
private async Task ScanSize()
|
||||
{
|
||||
if (_currentLocation?.Value is null) return;
|
||||
|
||||
var searchTask = _containerScanSnapshotProvider.StartSizeScan(_currentLocation.Value);
|
||||
var openContainerCommand = new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, searchTask.SizeContainer));
|
||||
await _userCommandHandlerService.HandleCommandAsync(openContainerCommand);
|
||||
}
|
||||
|
||||
private async Task SortItems(SortItemsCommand sortItemsCommand)
|
||||
{
|
||||
if (_currentSelectedTab is null) return;
|
||||
|
||||
@@ -27,6 +27,7 @@ public static class Startup
|
||||
serviceCollection.TryAddSingleton<IItemPreviewService, ItemPreviewService>();
|
||||
serviceCollection.TryAddSingleton<ITimelineViewModel, TimelineViewModel>();
|
||||
serviceCollection.TryAddSingleton<IRefreshSmoothnessCalculator, RefreshSmoothnessCalculator>();
|
||||
serviceCollection.TryAddSingleton<IItemPreviewProvider, ElementPreviewProvider>();
|
||||
|
||||
return serviceCollection
|
||||
.AddCommandHandlers()
|
||||
|
||||
@@ -48,6 +48,7 @@ public class DefaultIdentifiableCommandHandlerRegister : IStartupHandler
|
||||
AddUserCommand(RefreshCommand.Instance);
|
||||
AddUserCommand(RenameCommand.Instance);
|
||||
AddUserCommand(RunOrOpenCommand.Instance);
|
||||
AddUserCommand(ScanSizeCommand.Instance);
|
||||
AddUserCommand(StartCommandSchedulerCommand.Instance);
|
||||
AddUserCommand(SortItemsCommand.OrderByNameCommand);
|
||||
AddUserCommand(SortItemsCommand.OrderByNameDescCommand);
|
||||
|
||||
@@ -7,13 +7,14 @@ using MvvmGen;
|
||||
namespace FileTime.App.Core.ViewModels.ItemPreview;
|
||||
|
||||
[ViewModel]
|
||||
public partial class ElementPreviewViewModel : IItemPreviewViewModel, IAsyncInitable<IElement>
|
||||
public partial class ElementPreviewViewModel : IElementPreviewViewModel, IAsyncInitable<IElement>
|
||||
{
|
||||
public const string PreviewName = "ElementPreview";
|
||||
private record EncodingResult(char BinaryChar, string PartialResult);
|
||||
|
||||
private const int MaxTextPreviewSize = 1024 * 1024;
|
||||
|
||||
private static readonly List<Encoding> _encodings = new()
|
||||
private static readonly List<Encoding> Encodings = new()
|
||||
{
|
||||
Encoding.UTF8,
|
||||
Encoding.Unicode,
|
||||
@@ -27,6 +28,8 @@ public partial class ElementPreviewViewModel : IItemPreviewViewModel, IAsyncInit
|
||||
[Property] private string? _textContent;
|
||||
[Property] private string? _textEncoding;
|
||||
|
||||
public string Name => PreviewName;
|
||||
|
||||
public async Task InitAsync(IElement element)
|
||||
{
|
||||
try
|
||||
@@ -59,7 +62,7 @@ public partial class ElementPreviewViewModel : IItemPreviewViewModel, IAsyncInit
|
||||
(string, Encoding?) GetNormalizedText(byte[] data)
|
||||
{
|
||||
var binaryCharacter = new Dictionary<string, EncodingResult>();
|
||||
foreach (var encoding in _encodings)
|
||||
foreach (var encoding in Encodings)
|
||||
{
|
||||
var text = encoding.GetString(data);
|
||||
var binary = false;
|
||||
|
||||
Reference in New Issue
Block a user