Contaienr size scan WIP

This commit is contained in:
2023-08-02 08:25:19 +02:00
parent c95be170ed
commit 1713973c3a
35 changed files with 760 additions and 125 deletions

View File

@@ -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>

View File

@@ -0,0 +1,9 @@
using FileTime.Core.ContentAccess;
using FileTime.Core.Models;
namespace FileTime.App.ContainerSizeScanner;
public interface IContainerScanSnapshotProvider : IContentProvider
{
ISizeScanTask StartSizeScan(IContainer scanSizeOf);
}

View File

@@ -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; }
}

View File

@@ -0,0 +1,8 @@
using FileTime.Core.Models;
namespace FileTime.App.ContainerSizeScanner;
public interface ISizeItem : IItem
{
}

View File

@@ -0,0 +1,8 @@
using FileTime.App.Core.ViewModels.ItemPreview;
namespace FileTime.App.ContainerSizeScanner;
public interface ISizePreviewItem : IItemPreviewViewModel
{
}

View File

@@ -0,0 +1,10 @@
using FileTime.Core.Models;
using InitableService;
namespace FileTime.App.ContainerSizeScanner;
public interface ISizeScanTask : IInitable<IContainer>
{
IContainerSizeScanContainer SizeContainer { get; }
void Start();
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -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 };
}

View File

@@ -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>

View 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);
}
}
}

View 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;
}
}

View File

@@ -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);
}

View File

@@ -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";
}

View File

@@ -0,0 +1,8 @@
using FileTime.App.Core.Models;
namespace FileTime.App.Core.ViewModels.ItemPreview;
public interface IElementPreviewViewModel : IItemPreviewViewModel
{
ItemPreviewMode Mode { get; }
}

View File

@@ -1,8 +1,6 @@
using FileTime.App.Core.Models;
namespace FileTime.App.Core.ViewModels.ItemPreview;
public interface IItemPreviewViewModel
{
ItemPreviewMode Mode { get; }
string Name { get; }
}

View File

@@ -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" />

View File

@@ -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>();
}
}

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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()

View File

@@ -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);

View File

@@ -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;