Container size scan, improvements
This commit is contained in:
@@ -1,13 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.App.Core.Models.Traits;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public interface ISizeItem : IItem
|
||||
public interface ISizeItem : IItem, ISizeProvider
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||
using DeclarativeProperty;
|
||||
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public interface ISizePreviewItem : IItemPreviewViewModel
|
||||
public interface ISizePreviewItem
|
||||
{
|
||||
|
||||
IDeclarativeProperty<long> Size { get; }
|
||||
string Name { get; }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using DeclarativeProperty;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Models.ContainerTraits;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public interface ISizeScanContainer : ISizeItem, IContainer, IStatusProviderContainer
|
||||
{
|
||||
public Task AddSizeSourceAsync(IDeclarativeProperty<long> sizeElement);
|
||||
ObservableCollection<ISizeScanContainer> ChildContainers { get; }
|
||||
ObservableCollection<ISizeScanElement> ChildElements { get; }
|
||||
ObservableCollection<ISizeItem> SizeItems { get; }
|
||||
IContainer RealContainer { get; init; }
|
||||
Task StartLoadingAsync();
|
||||
Task StopLoadingAsync();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public interface ISizeScanElement : ISizeItem, IElement
|
||||
{
|
||||
|
||||
}
|
||||
@@ -5,6 +5,8 @@ namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public interface ISizeScanTask : IInitable<IContainer>
|
||||
{
|
||||
IContainerSizeScanContainer SizeContainer { get; }
|
||||
ISizeScanContainer SizeSizeScanContainer { get; }
|
||||
bool IsRunning { get; }
|
||||
void Start();
|
||||
void Stop();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using DeclarativeProperty;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public class ChildContainerPreview : ISizePreviewItem
|
||||
{
|
||||
public ChildContainerPreview(ISizeScanContainer container)
|
||||
{
|
||||
Name = container.Name;
|
||||
Size = container.Size;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public IDeclarativeProperty<long> Size { get; }
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using DeclarativeProperty;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public class ChildElementPreview : ISizePreviewItem
|
||||
{
|
||||
public ChildElementPreview(ISizeScanElement element)
|
||||
{
|
||||
Name = element.Name;
|
||||
Size = element.Size;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public IDeclarativeProperty<long> Size { get; }
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||
using ObservableComputations;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public class ContainerPreview : IItemPreviewViewModel, IDisposable
|
||||
{
|
||||
private readonly OcConsumer _topItemsConsumer = new();
|
||||
public const string PreviewName = "SizePreviewContainer";
|
||||
public string Name => PreviewName;
|
||||
|
||||
public ObservableCollection<ISizePreviewItem> TopItems { get; }
|
||||
|
||||
public ContainerPreview(ISizeScanContainer sizeScanContainer)
|
||||
{
|
||||
TopItems = sizeScanContainer
|
||||
.SizeItems
|
||||
.Ordering(c => c.Size.Value, ListSortDirection.Descending)
|
||||
.Taking(0, 10)
|
||||
.Selecting(i => CreatePreviewItem(i))
|
||||
.For(_topItemsConsumer);
|
||||
}
|
||||
|
||||
private ISizePreviewItem CreatePreviewItem(ISizeItem sizeItem)
|
||||
=> sizeItem switch
|
||||
{
|
||||
ISizeScanContainer container => new ChildContainerPreview(container),
|
||||
ISizeScanElement element => new ChildElementPreview(element),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(sizeItem))
|
||||
};
|
||||
|
||||
public void Dispose()
|
||||
=> _topItemsConsumer.Dispose();
|
||||
}
|
||||
@@ -7,14 +7,14 @@ using InitableService;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public class ContainerScanSnapshotProvider : ContentProviderBase, IContainerScanSnapshotProvider
|
||||
public class ContainerScanProvider : 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(
|
||||
public ContainerScanProvider(
|
||||
ITimelessContentProvider timelessContentProvider,
|
||||
IServiceProvider serviceProvider)
|
||||
: base(ContentProviderName, timelessContentProvider)
|
||||
@@ -36,7 +36,7 @@ public class ContainerScanSnapshotProvider : ContentProviderBase, IContainerScan
|
||||
|
||||
var pathParts = fullName.Path.Split(Constants.SeparatorChar);
|
||||
|
||||
var item = _sizeScanTasks.FirstOrDefault(t => t.SizeContainer.Name == pathParts[1])?.SizeContainer;
|
||||
var item = _sizeScanTasks.FirstOrDefault(t => t.SizeSizeScanContainer.Name == pathParts[1])?.SizeSizeScanContainer;
|
||||
|
||||
if (pathParts.Length == 2)
|
||||
return item ?? throw new ItemNotFoundException(fullName);
|
||||
@@ -49,10 +49,10 @@ public class ContainerScanSnapshotProvider : ContentProviderBase, IContainerScan
|
||||
|
||||
if (item is not null)
|
||||
{
|
||||
var container = item.ChildContainers.FirstOrDefault(c => c.Name == pathParts[^1]);
|
||||
if (container is not null) return container;
|
||||
var childItem = item.SizeItems.FirstOrDefault(c => c.Name == pathParts[^1]);
|
||||
if (childItem is not null) return childItem;
|
||||
|
||||
var childName = item.RealContainer.FullName?.GetChild(pathParts[^1]);
|
||||
/*var childName = item.RealContainer.FullName?.GetChild(pathParts[^1]);
|
||||
if (childName is null) throw new ItemNotFoundException(fullName);
|
||||
|
||||
return await _timelessContentProvider.GetItemByFullNameAsync(
|
||||
@@ -61,7 +61,7 @@ public class ContainerScanSnapshotProvider : ContentProviderBase, IContainerScan
|
||||
forceResolve,
|
||||
forceResolvePathType,
|
||||
itemInitializationSettings
|
||||
);
|
||||
);*/
|
||||
}
|
||||
|
||||
throw new ItemNotFoundException(fullName);
|
||||
@@ -106,7 +106,7 @@ public class ContainerScanSnapshotProvider : ContentProviderBase, IContainerScan
|
||||
|
||||
_sizeScanTasks.Add(searchTask);
|
||||
searchTask.Start();
|
||||
Items.Add(new AbsolutePath(_timelessContentProvider, searchTask.SizeContainer));
|
||||
Items.Add(new AbsolutePath(_timelessContentProvider, searchTask.SizeSizeScanContainer));
|
||||
|
||||
return searchTask;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
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,17 @@
|
||||
using FileTime.App.Core.Services;
|
||||
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public class PreviewProvider : IItemPreviewProvider
|
||||
{
|
||||
public bool CanHandle(IItem item) => item is SizeScanContainer;
|
||||
|
||||
public Task<IItemPreviewViewModel> CreatePreviewAsync(IItem item)
|
||||
{
|
||||
if (item is not SizeScanContainer container) throw new NotSupportedException();
|
||||
|
||||
return Task.FromResult((IItemPreviewViewModel) new ContainerPreview(container));
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,13 @@ using DeclarativeProperty;
|
||||
using FileTime.Core.ContentAccess;
|
||||
using FileTime.Core.Enums;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Models.ContainerTraits;
|
||||
using FileTime.Core.Timeline;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
//TODO: create readonly version
|
||||
public class ContainerSizeScanContainer : IContainerSizeScanContainer
|
||||
public record SizeScanContainer : ISizeScanContainer, IEscHandlerContainer
|
||||
{
|
||||
private readonly ReadOnlyExtensionCollection _readOnlyExtensions;
|
||||
private readonly BehaviorSubject<bool> _isLoading = new(false);
|
||||
@@ -19,20 +20,20 @@ public class ContainerSizeScanContainer : IContainerSizeScanContainer
|
||||
public required string DisplayName { get; init; }
|
||||
public required FullName? FullName { get; init; }
|
||||
public required NativePath? NativePath { get; init; }
|
||||
public AbsolutePath? Parent { get; init; }
|
||||
public required AbsolutePath? Parent { get; init; }
|
||||
public required IContentProvider Provider { get; init; }
|
||||
public bool IsHidden => false;
|
||||
public bool IsExists => true;
|
||||
public DateTime? CreatedAt { get; }
|
||||
public DateTime? CreatedAt { get; } = DateTime.Now;
|
||||
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 IItem WithParent(AbsolutePath parent) => this with {Parent = parent};
|
||||
|
||||
public ObservableCollection<AbsolutePath> Items { get; } = new();
|
||||
public IObservable<bool> IsLoading { get; }
|
||||
@@ -41,18 +42,21 @@ public class ContainerSizeScanContainer : IContainerSizeScanContainer
|
||||
|
||||
public bool AllowRecursiveDeletion => false;
|
||||
|
||||
public IDeclarativeProperty<long> Size => _size;
|
||||
public ObservableCollection<IContainerSizeScanContainer> ChildContainers { get; } = new();
|
||||
public IDeclarativeProperty<long> Size { get; }
|
||||
|
||||
public ObservableCollection<ISizeScanContainer> ChildContainers { get; } = new();
|
||||
public ObservableCollection<ISizeScanElement> ChildElements { get; } = new();
|
||||
public ObservableCollection<ISizeItem> SizeItems { get; } = new();
|
||||
public required IContainer RealContainer { get; init; }
|
||||
|
||||
public ContainerSizeScanContainer(IContainerScanSnapshotProvider provider)
|
||||
internal SizeScanContainer()
|
||||
{
|
||||
_readOnlyExtensions = new ReadOnlyExtensionCollection(Extensions);
|
||||
IsLoading = _isLoading.AsObservable();
|
||||
|
||||
_size = new(childContainerSizes => Task.FromResult(childContainerSizes.Sum()));
|
||||
Size = _size.Debounce(TimeSpan.FromSeconds(1));
|
||||
CreatedAt = DateTime.Now;
|
||||
Provider = provider;
|
||||
}
|
||||
|
||||
public async Task AddSizeSourceAsync(IDeclarativeProperty<long> sizeElement)
|
||||
@@ -71,4 +75,19 @@ public class ContainerSizeScanContainer : IContainerSizeScanContainer
|
||||
IsLoaded = true;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public required IDeclarativeProperty<string> Status { get; init; } = new DeclarativeProperty<string>();
|
||||
|
||||
public required SizeScanTask SizeScanTask { get; init; }
|
||||
|
||||
public Task<ContainerEscapeResult> HandleEsc()
|
||||
{
|
||||
if (!SizeScanTask.IsRunning)
|
||||
{
|
||||
return Task.FromResult(new ContainerEscapeResult(false));
|
||||
}
|
||||
|
||||
SizeScanTask.Stop();
|
||||
return Task.FromResult(new ContainerEscapeResult(true));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using DeclarativeProperty;
|
||||
using FileTime.Core.ContentAccess;
|
||||
using FileTime.Core.Enums;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Timeline;
|
||||
|
||||
namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public record SizeScanElement : ISizeScanElement
|
||||
{
|
||||
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 required IDeclarativeProperty<long> Size { get; init; }
|
||||
public bool IsHidden => false;
|
||||
public bool IsExists => true;
|
||||
public DateTime? CreatedAt { get; } = DateTime.Now;
|
||||
public SupportsDelete CanDelete => SupportsDelete.False;
|
||||
public bool CanRename => false;
|
||||
public string? Attributes => "";
|
||||
public required IContentProvider Provider { get; init; }
|
||||
public PointInTime PointInTime { get; } = PointInTime.Present;
|
||||
public ObservableCollection<Exception> Exceptions { get; } = new();
|
||||
public ReadOnlyExtensionCollection Extensions { get; } = new();
|
||||
public AbsolutePathType Type => AbsolutePathType.Element;
|
||||
|
||||
public IItem WithParent(AbsolutePath parent) => this with {Parent = parent};
|
||||
}
|
||||
@@ -9,13 +9,19 @@ namespace FileTime.App.ContainerSizeScanner;
|
||||
|
||||
public class SizeScanTask : ISizeScanTask
|
||||
{
|
||||
private bool _cancelled;
|
||||
private int _processedItems;
|
||||
private ulong _processedItemsTotal;
|
||||
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!;
|
||||
private readonly DeclarativeProperty<string> _containerStatus = new();
|
||||
private readonly IDeclarativeProperty<string> _containerStatusDebounced;
|
||||
public ISizeScanContainer SizeSizeScanContainer { get; private set; } = null!;
|
||||
public bool IsRunning { get; private set; }
|
||||
|
||||
public SizeScanTask(
|
||||
IContainerScanSnapshotProvider containerScanSnapshotProvider,
|
||||
@@ -25,21 +31,25 @@ public class SizeScanTask : ISizeScanTask
|
||||
_containerScanSnapshotProvider = containerScanSnapshotProvider;
|
||||
_timelessContentProvider = timelessContentProvider;
|
||||
_logger = logger;
|
||||
_containerStatusDebounced = _containerStatus.Debounce(TimeSpan.FromMilliseconds(250));
|
||||
}
|
||||
|
||||
public void Init(IContainer scanSizeOf)
|
||||
{
|
||||
_scanSizeOf = scanSizeOf;
|
||||
var name = $"{_searchId++}_{scanSizeOf.Name}";
|
||||
var randomId = ContainerScanSnapshotProvider.ContentProviderName + Constants.SeparatorChar + name;
|
||||
SizeContainer = new ContainerSizeScanContainer(_containerScanSnapshotProvider)
|
||||
var randomId = ContainerScanProvider.ContentProviderName + Constants.SeparatorChar + name;
|
||||
SizeSizeScanContainer = new SizeScanContainer
|
||||
{
|
||||
Name = name,
|
||||
DisplayName = scanSizeOf.DisplayName,
|
||||
FullName = new FullName(randomId),
|
||||
NativePath = new NativePath(randomId),
|
||||
Parent = new AbsolutePath(_timelessContentProvider, _containerScanSnapshotProvider),
|
||||
RealContainer = scanSizeOf
|
||||
RealContainer = scanSizeOf,
|
||||
Provider = _containerScanSnapshotProvider,
|
||||
Status = _containerStatusDebounced,
|
||||
SizeScanTask = this
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,23 +62,34 @@ public class SizeScanTask : ISizeScanTask
|
||||
_sizeScanThread = sizeScanThread;
|
||||
}
|
||||
|
||||
public void Stop() => _cancelled = true;
|
||||
|
||||
private async void Run()
|
||||
{
|
||||
try
|
||||
{
|
||||
await TraverseTree(_scanSizeOf, SizeContainer);
|
||||
IsRunning = true;
|
||||
await SizeSizeScanContainer.StartLoadingAsync();
|
||||
await TraverseTree(_scanSizeOf, SizeSizeScanContainer);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error while scanning container {ContainerName}", _scanSizeOf.Name);
|
||||
SizeSizeScanContainer.Exceptions.Add(ex);
|
||||
}
|
||||
|
||||
IsRunning = false;
|
||||
await SizeSizeScanContainer.StopLoadingAsync();
|
||||
}
|
||||
|
||||
//TODO: make static
|
||||
private async Task TraverseTree(
|
||||
IContainer realContainer,
|
||||
IContainerSizeScanContainer container)
|
||||
ISizeScanContainer sizeScanContainer)
|
||||
{
|
||||
if(_cancelled) return;
|
||||
|
||||
await realContainer.WaitForLoaded();
|
||||
var resolvedItems = new List<IItem>(realContainer.Items.Count);
|
||||
foreach (var item in realContainer.Items)
|
||||
{
|
||||
@@ -78,34 +99,59 @@ public class SizeScanTask : ISizeScanTask
|
||||
|
||||
foreach (var element in resolvedItems.OfType<IElement>())
|
||||
{
|
||||
if(_cancelled) return;
|
||||
|
||||
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(
|
||||
var sizeProperty = new DeclarativeProperty<long>(size);
|
||||
|
||||
var childName = sizeScanContainer.FullName!.GetChild(element.Name).Path;
|
||||
await sizeScanContainer.AddSizeSourceAsync(sizeProperty);
|
||||
sizeScanContainer.Items.Add(new AbsolutePath(
|
||||
_timelessContentProvider,
|
||||
PointInTime.Present,
|
||||
new FullName(childName),
|
||||
AbsolutePathType.Element));
|
||||
|
||||
|
||||
var childSearchContainer = new SizeScanElement
|
||||
{
|
||||
Name = element.Name,
|
||||
DisplayName = element.DisplayName,
|
||||
FullName = new FullName(childName),
|
||||
NativePath = new NativePath(childName),
|
||||
Parent = new AbsolutePath(_timelessContentProvider, sizeScanContainer),
|
||||
Provider = _containerScanSnapshotProvider,
|
||||
Size = sizeProperty
|
||||
};
|
||||
sizeScanContainer.SizeItems.Add(childSearchContainer);
|
||||
_processedItems++;
|
||||
_processedItemsTotal++;
|
||||
}
|
||||
|
||||
foreach (var childContainer in resolvedItems.OfType<IContainer>())
|
||||
{
|
||||
var childName = container.FullName!.GetChild(childContainer.Name).Path;
|
||||
var childSearchContainer = new ContainerSizeScanContainer(_containerScanSnapshotProvider)
|
||||
if(_cancelled) return;
|
||||
|
||||
var childName = sizeScanContainer.FullName!.GetChild(childContainer.Name).Path;
|
||||
var childSearchContainer = new SizeScanContainer
|
||||
{
|
||||
Name = childContainer.Name,
|
||||
DisplayName = childContainer.DisplayName,
|
||||
FullName = new FullName(childName),
|
||||
NativePath = new NativePath(childName),
|
||||
Parent = new AbsolutePath(_timelessContentProvider, container),
|
||||
RealContainer = childContainer
|
||||
Parent = new AbsolutePath(_timelessContentProvider, sizeScanContainer),
|
||||
RealContainer = childContainer,
|
||||
Provider = _containerScanSnapshotProvider,
|
||||
Status = _containerStatusDebounced,
|
||||
SizeScanTask = this
|
||||
};
|
||||
|
||||
container.ChildContainers.Add(childSearchContainer);
|
||||
await container.AddSizeSourceAsync(childSearchContainer.Size);
|
||||
container.Items.Add(new AbsolutePath(
|
||||
sizeScanContainer.ChildContainers.Add(childSearchContainer);
|
||||
sizeScanContainer.SizeItems.Add(childSearchContainer);
|
||||
await sizeScanContainer.AddSizeSourceAsync(childSearchContainer.Size);
|
||||
sizeScanContainer.Items.Add(new AbsolutePath(
|
||||
_timelessContentProvider,
|
||||
PointInTime.Present,
|
||||
new FullName(childName),
|
||||
@@ -113,5 +159,16 @@ public class SizeScanTask : ISizeScanTask
|
||||
|
||||
await TraverseTree(childContainer, childSearchContainer);
|
||||
}
|
||||
|
||||
_processedItems++;
|
||||
_processedItemsTotal++;
|
||||
await _containerStatus.SetValue("Items processed: " + _processedItemsTotal);
|
||||
|
||||
if (_processedItems > 1000)
|
||||
{
|
||||
_processedItems = 0;
|
||||
//Let some time for the UI to refresh
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,10 @@ public static class Startup
|
||||
{
|
||||
public static IServiceCollection AddContainerSizeScanner(this IServiceCollection services)
|
||||
{
|
||||
services.TryAddSingleton<IContainerScanSnapshotProvider, ContainerScanSnapshotProvider>();
|
||||
services.TryAddSingleton<IContainerScanSnapshotProvider, ContainerScanProvider>();
|
||||
services.AddSingleton<IContentProvider>(sp => sp.GetRequiredService<IContainerScanSnapshotProvider>());
|
||||
services.AddTransient<ISizeScanTask, SizeScanTask>();
|
||||
services.AddTransient<IItemPreviewProvider, ContainerSizePreviewProvider>();
|
||||
services.AddTransient<IItemPreviewProvider, PreviewProvider>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,6 @@ public class DoubleTextPreview : IPreviewElement
|
||||
public IObservable<List<ItemNamePart>> Text1 { get; init; } = new BehaviorSubject<List<ItemNamePart>>(new());
|
||||
public IObservable<List<ItemNamePart>> Text2 { get; init; } = new BehaviorSubject<List<ItemNamePart>>(new());
|
||||
|
||||
public PreviewType PreviewType { get; } = PreviewType.DoubleTextList;
|
||||
public PreviewType PreviewType => PreviewType.DoubleTextList;
|
||||
object IPreviewElement.PreviewType => PreviewType;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace FileTime.App.Core.Models.Enums;
|
||||
|
||||
public enum ItemAttributeType
|
||||
{
|
||||
File,
|
||||
Element,
|
||||
Container,
|
||||
SizeContainer
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using DeclarativeProperty;
|
||||
|
||||
namespace FileTime.App.Core.Models.Traits;
|
||||
|
||||
public interface ISizeProvider
|
||||
{
|
||||
IDeclarativeProperty<long> Size { get; }
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
using DeclarativeProperty;
|
||||
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||
|
||||
namespace FileTime.App.Core.Services;
|
||||
|
||||
public interface IItemPreviewService
|
||||
{
|
||||
IObservable<IItemPreviewViewModel?> ItemPreview { get; }
|
||||
IDeclarativeProperty<IItemPreviewViewModel?> ItemPreview { get; }
|
||||
}
|
||||
@@ -13,6 +13,7 @@ public interface IAppState
|
||||
IDeclarativeProperty<ViewMode> ViewMode { get; }
|
||||
DeclarativeProperty<string?> RapidTravelText { get; }
|
||||
ITimelineViewModel TimelineViewModel { get; }
|
||||
IDeclarativeProperty<string?> ContainerStatus { get; }
|
||||
|
||||
void AddTab(ITabViewModel tabViewModel);
|
||||
void RemoveTab(ITabViewModel tabViewModel);
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
using FileTime.App.Core.Models.Enums;
|
||||
using FileTime.Core.Models;
|
||||
using InitableService;
|
||||
|
||||
namespace FileTime.App.Core.ViewModels;
|
||||
|
||||
public interface IContainerSizeContainerViewModel : IItemViewModel, IInitable<IContainer, ITabViewModel, ItemViewModelType>
|
||||
{
|
||||
long Size { get; set; }
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
using FileTime.App.Core.Models.Enums;
|
||||
using FileTime.App.Core.Models.Traits;
|
||||
using FileTime.Core.Models;
|
||||
using InitableService;
|
||||
|
||||
namespace FileTime.App.Core.ViewModels;
|
||||
|
||||
public interface IElementViewModel : IItemViewModel, IInitable<IElement, ITabViewModel, ItemViewModelType>
|
||||
public interface IElementViewModel :
|
||||
IItemViewModel,
|
||||
ISizeProvider,
|
||||
IInitable<IElement, ITabViewModel, ItemViewModelType>
|
||||
{
|
||||
IElement? Element { get; }
|
||||
long? Size { get; set; }
|
||||
}
|
||||
@@ -13,7 +13,7 @@ public interface IItemViewModel : IInitable<IItem, ITabViewModel, ItemViewModelT
|
||||
IDeclarativeProperty<bool> IsSelected { get; set; }
|
||||
IDeclarativeProperty<bool>? IsMarked { get; set; }
|
||||
IDeclarativeProperty<bool> IsAlternative { get; }
|
||||
IObservable<ItemViewMode> ViewMode { get; set; }
|
||||
IDeclarativeProperty<ItemViewMode> ViewMode { get; set; }
|
||||
DateTime? CreatedAt { get; set; }
|
||||
string? Attributes { get; set; }
|
||||
bool EqualsTo(IItemViewModel? itemViewModel);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Reactive.Linq;
|
||||
using DeclarativeProperty;
|
||||
using FileTime.App.Core.ViewModels;
|
||||
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||
using FileTime.Core.Models;
|
||||
@@ -10,7 +11,7 @@ public class ItemPreviewService : IItemPreviewService
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IEnumerable<IItemPreviewProvider> _itemPreviewProviders;
|
||||
public IObservable<IItemPreviewViewModel?> ItemPreview { get; }
|
||||
public IDeclarativeProperty<IItemPreviewViewModel?> ItemPreview { get; }
|
||||
|
||||
public ItemPreviewService(
|
||||
IAppState appState,
|
||||
@@ -21,18 +22,15 @@ public class ItemPreviewService : IItemPreviewService
|
||||
_itemPreviewProviders = itemPreviewProviders;
|
||||
ItemPreview = appState
|
||||
.SelectedTab
|
||||
.Select(t =>
|
||||
t?.CurrentSelectedItem.Throttle(TimeSpan.FromMilliseconds(250))
|
||||
?? Observable.Return<IItemViewModel?>(null))
|
||||
.Map(t => t.CurrentSelectedItem)
|
||||
.Switch()
|
||||
.Select(item =>
|
||||
.Debounce(TimeSpan.FromMilliseconds(250))
|
||||
.Map(async (item, _) =>
|
||||
item == null
|
||||
? Observable.Return<IItemPreviewViewModel?>(null)
|
||||
: Observable.FromAsync(async () => await Map(item))
|
||||
? null
|
||||
: await Map(item)
|
||||
)
|
||||
.Switch()
|
||||
.Publish(null)
|
||||
.RefCount();
|
||||
.DistinctUntilChanged();
|
||||
}
|
||||
|
||||
private async Task<IItemPreviewViewModel?> Map(IItemViewModel itemViewModel)
|
||||
|
||||
@@ -112,6 +112,7 @@ public class TabPersistenceService : ITabPersistenceService
|
||||
try
|
||||
{
|
||||
if (tab.Path == null) continue;
|
||||
if (_contentProvidersNotToRestore.Any(p => tab.Path.StartsWith(p))) continue;
|
||||
|
||||
IContainer? container = null;
|
||||
var path = FullName.CreateSafe(tab.Path);
|
||||
@@ -186,7 +187,7 @@ public class TabPersistenceService : ITabPersistenceService
|
||||
public void SaveStates(CancellationToken token = default)
|
||||
{
|
||||
var state = new PersistenceRoot(SerializeTabStates());
|
||||
|
||||
|
||||
var settingsDirectory = new DirectoryInfo(string.Join(Path.DirectorySeparatorChar,
|
||||
_settingsPath.Split(Path.DirectorySeparatorChar)[0..^1]));
|
||||
if (!settingsDirectory.Exists) settingsDirectory.Create();
|
||||
|
||||
@@ -332,7 +332,11 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
}
|
||||
else if (tabViewModel == null)
|
||||
{
|
||||
var tab = await _serviceProvider.GetAsyncInitableResolver<IContainer>(_currentLocation?.Value ?? _localContentProvider)
|
||||
var newLocation = _currentLocation?.Value?.FullName is { } fullName
|
||||
? (IContainer) await _timelessContentProvider.GetItemByFullNameAsync(fullName, PointInTime.Present)
|
||||
: _localContentProvider;
|
||||
|
||||
var tab = await _serviceProvider.GetAsyncInitableResolver<IContainer>(newLocation)
|
||||
.GetRequiredServiceAsync<ITab>();
|
||||
var newTabViewModel = _serviceProvider.GetInitableResolver(tab, number).GetRequiredService<ITabViewModel>();
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
if (_currentLocation?.Value is null) return;
|
||||
|
||||
var searchTask = _containerScanSnapshotProvider.StartSizeScan(_currentLocation.Value);
|
||||
var openContainerCommand = new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, searchTask.SizeContainer));
|
||||
var openContainerCommand = new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, searchTask.SizeSizeScanContainer));
|
||||
await _userCommandHandlerService.HandleCommandAsync(openContainerCommand);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ public static class Startup
|
||||
serviceCollection.TryAddTransient<IContainerViewModel, ContainerViewModel>();
|
||||
serviceCollection.TryAddTransient<IElementViewModel, ElementViewModel>();
|
||||
serviceCollection.TryAddTransient<IFileViewModel, FileViewModel>();
|
||||
serviceCollection.TryAddTransient<IContainerSizeContainerViewModel, ContainerSizeContainerViewModel>();
|
||||
serviceCollection.TryAddTransient<IItemNameConverterService, ItemNameConverterService>();
|
||||
serviceCollection.TryAddTransient<ElementPreviewViewModel>();
|
||||
serviceCollection.TryAddSingleton<IUserCommandHandlerService, UserCommandHandlerService>();
|
||||
|
||||
@@ -5,6 +5,8 @@ using DeclarativeProperty;
|
||||
using DynamicData;
|
||||
using FileTime.App.Core.Models.Enums;
|
||||
using FileTime.App.Core.ViewModels.Timeline;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Models.ContainerTraits;
|
||||
using MvvmGen;
|
||||
using MoreLinq;
|
||||
|
||||
@@ -27,6 +29,8 @@ public abstract partial class AppStateBase : IAppState
|
||||
public IDeclarativeProperty<ITabViewModel?> SelectedTab { get; private set; }
|
||||
public DeclarativeProperty<string?> RapidTravelText { get; private set; }
|
||||
|
||||
public IDeclarativeProperty<string?> ContainerStatus { get; private set; }
|
||||
|
||||
partial void OnInitialize()
|
||||
{
|
||||
RapidTravelText = new("");
|
||||
@@ -40,6 +44,12 @@ public abstract partial class AppStateBase : IAppState
|
||||
);
|
||||
|
||||
Tabs = new ReadOnlyObservableCollection<ITabViewModel>(_tabs);
|
||||
|
||||
ContainerStatus = SelectedTab
|
||||
.Map(t => t?.CurrentLocation)
|
||||
.Switch()
|
||||
.Map(c => c is IStatusProviderContainer statusProvider ? statusProvider.Status : null)
|
||||
.Switch();
|
||||
}
|
||||
|
||||
public void AddTab(ITabViewModel tabViewModel)
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
using FileTime.App.Core.Models.Enums;
|
||||
using FileTime.App.Core.Services;
|
||||
using FileTime.Core.Models;
|
||||
using MvvmGen;
|
||||
|
||||
namespace FileTime.App.Core.ViewModels;
|
||||
|
||||
[ViewModel(GenerateConstructor = false)]
|
||||
public partial class ContainerSizeContainerViewModel : ItemViewModel, IContainerSizeContainerViewModel
|
||||
{
|
||||
[Property]
|
||||
private long _size;
|
||||
|
||||
public ContainerSizeContainerViewModel(IItemNameConverterService itemNameConverterService, IAppState appState) : base(itemNameConverterService, appState)
|
||||
{
|
||||
}
|
||||
|
||||
public void Init(IContainer item, ITabViewModel parentTab, ItemViewModelType itemViewModelType)
|
||||
=> Init((IItem)item, parentTab, itemViewModelType);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using DeclarativeProperty;
|
||||
using FileTime.App.Core.Models.Enums;
|
||||
using FileTime.App.Core.Services;
|
||||
using FileTime.Core.Models;
|
||||
@@ -9,9 +10,6 @@ namespace FileTime.App.Core.ViewModels;
|
||||
public partial class ElementViewModel : ItemViewModel, IElementViewModel
|
||||
{
|
||||
public IElement? Element => BaseItem as Element;
|
||||
|
||||
[Property]
|
||||
private long? _size;
|
||||
|
||||
public ElementViewModel(IItemNameConverterService itemNameConverterService, IAppState appState) : base(itemNameConverterService, appState)
|
||||
{
|
||||
@@ -19,4 +17,6 @@ public partial class ElementViewModel : ItemViewModel, IElementViewModel
|
||||
|
||||
public void Init(IElement item, ITabViewModel parentTab, ItemViewModelType itemViewModelType)
|
||||
=> Init((IItem)item, parentTab, itemViewModelType);
|
||||
|
||||
public IDeclarativeProperty<long> Size { get; protected set; } = new DeclarativeProperty<long>(0);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using DeclarativeProperty;
|
||||
using FileTime.App.Core.Models.Enums;
|
||||
using FileTime.App.Core.Services;
|
||||
using FileTime.Core.Models;
|
||||
@@ -15,7 +16,7 @@ public partial class FileViewModel : ElementViewModel, IFileViewModel
|
||||
|
||||
public void Init(IElement item, FileExtension fileExtension, ITabViewModel parentTab, ItemViewModelType itemViewModelType)
|
||||
{
|
||||
Init((IElement)item, parentTab, itemViewModelType);
|
||||
Size = fileExtension.Size;
|
||||
Init(item, parentTab, itemViewModelType);
|
||||
Size = new DeclarativeProperty<long>(fileExtension.Size ?? 0);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Reactive.Linq;
|
||||
using DeclarativeProperty;
|
||||
@@ -27,7 +28,7 @@ public abstract partial class ItemViewModel : IItemViewModel
|
||||
|
||||
[Property] private IDeclarativeProperty<bool>? _isMarked;
|
||||
|
||||
[Property] private IObservable<ItemViewMode> _viewMode;
|
||||
[Property] private IDeclarativeProperty<ItemViewMode> _viewMode;
|
||||
|
||||
[Property] private DateTime? _createdAt;
|
||||
|
||||
@@ -38,8 +39,8 @@ public abstract partial class ItemViewModel : IItemViewModel
|
||||
public IDeclarativeProperty<IReadOnlyList<ItemNamePart>>? DisplayName { get; private set; }
|
||||
|
||||
public void Init(
|
||||
IItem item,
|
||||
ITabViewModel parentTab,
|
||||
IItem item,
|
||||
ITabViewModel parentTab,
|
||||
ItemViewModelType itemViewModelType)
|
||||
{
|
||||
_parentTab = parentTab;
|
||||
@@ -55,7 +56,7 @@ public abstract partial class ItemViewModel : IItemViewModel
|
||||
var displayName = itemViewModelType switch
|
||||
{
|
||||
ItemViewModelType.Main => _appState.RapidTravelText.Map(async (s, _) =>
|
||||
_appState.ViewMode.Value != Models.Enums.ViewMode.RapidTravel
|
||||
_appState.ViewMode.Value != Models.Enums.ViewMode.RapidTravel
|
||||
&& _appState.SelectedTab.Value?.CurrentLocation.Value?.Provider is IItemNameConverterProvider nameConverterProvider
|
||||
? (IReadOnlyList<ItemNamePart>) await nameConverterProvider.GetItemNamePartsAsync(item)
|
||||
: _itemNameConverterService.GetDisplayName(item.DisplayName, s)
|
||||
@@ -75,15 +76,23 @@ public abstract partial class ItemViewModel : IItemViewModel
|
||||
? parentTab.CurrentSelectedItem.Map(EqualsTo)
|
||||
: new DeclarativeProperty<bool>(IsInDeepestPath());
|
||||
|
||||
IsAlternative = sourceCollection.Map(c => c?.Index().FirstOrDefault(i => EqualsTo(i.Value)).Key % 2 == 0);
|
||||
IsAlternative = sourceCollection
|
||||
.Debounce(TimeSpan.FromMilliseconds(100))
|
||||
.Map(c =>
|
||||
c?.Index().FirstOrDefault(i => EqualsTo(i.Value)).Key % 2 == 1
|
||||
);
|
||||
|
||||
ViewMode = Observable.CombineLatest(IsMarked, IsSelected, IsAlternative, GenerateViewMode).Throttle(TimeSpan.FromMilliseconds(10));
|
||||
ViewMode = DeclarativePropertyHelpers
|
||||
.CombineLatest(IsMarked, IsSelected, IsAlternative, GenerateViewMode)
|
||||
.DistinctUntilChanged()
|
||||
.Debounce(TimeSpan.FromMilliseconds(100));
|
||||
Attributes = item.Attributes;
|
||||
CreatedAt = item.CreatedAt;
|
||||
}
|
||||
|
||||
private ItemViewMode GenerateViewMode(bool isMarked, bool isSelected, bool isAlternative)
|
||||
=> (isMarked, isSelected, isAlternative) switch
|
||||
private Task<ItemViewMode> GenerateViewMode(bool isMarked, bool isSelected, bool isAlternative)
|
||||
{
|
||||
var result = (isMarked, isSelected, isAlternative) switch
|
||||
{
|
||||
(true, true, _) => ItemViewMode.MarkedSelected,
|
||||
(true, false, true) => ItemViewMode.MarkedAlternative,
|
||||
@@ -93,12 +102,13 @@ public abstract partial class ItemViewModel : IItemViewModel
|
||||
_ => ItemViewMode.Default
|
||||
};
|
||||
|
||||
|
||||
public bool EqualsTo(IItemViewModel? itemViewModel)
|
||||
{
|
||||
return BaseItem?.FullName?.Path is string path && path == itemViewModel?.BaseItem?.FullName?.Path;
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
|
||||
public bool EqualsTo(IItemViewModel? itemViewModel)
|
||||
=> BaseItem?.FullName?.Path is { } path && path == itemViewModel?.BaseItem?.FullName?.Path;
|
||||
|
||||
private bool IsInDeepestPath()
|
||||
{
|
||||
if (_parentTab?.Tab?.LastDeepestSelectedPath is null
|
||||
|
||||
@@ -162,7 +162,7 @@ public partial class TabViewModel : ITabViewModel
|
||||
.Selecting(i => MapItem(i))
|
||||
.Ordering(i => i.Type)
|
||||
.ThenOrdering(i => i.Name)
|
||||
.Selecting(i => MapItemToViewModel(i, ItemViewModelType.SelectedChild));
|
||||
.Selecting(i => MapItemToViewModel(i, ItemViewModelType.Parent));
|
||||
|
||||
return items;
|
||||
});
|
||||
|
||||
@@ -140,7 +140,7 @@ public partial class FrequencyNavigationService : IFrequencyNavigationService, I
|
||||
{
|
||||
return _containerScores
|
||||
.Where(c => c.Key.Contains(searchText, StringComparison.OrdinalIgnoreCase))
|
||||
.OrderBy(c => GetWeightedScore(c.Value.Score, c.Value.LastAccessed))
|
||||
.OrderByDescending(c => GetWeightedScore(c.Value.Score, c.Value.LastAccessed))
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user