Container traits to extensions
This commit is contained in:
@@ -1,9 +0,0 @@
|
|||||||
using FileTime.Core.ContentAccess;
|
|
||||||
using FileTime.Core.Models;
|
|
||||||
|
|
||||||
namespace FileTime.App.ContainerSizeScanner;
|
|
||||||
|
|
||||||
public interface IContainerScanSnapshotProvider : IContentProvider
|
|
||||||
{
|
|
||||||
ISizeScanTask StartSizeScan(IContainer scanSizeOf);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using FileTime.App.Core.Services;
|
||||||
|
using FileTime.Core.ContentAccess;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
|
||||||
|
namespace FileTime.App.ContainerSizeScanner;
|
||||||
|
|
||||||
|
public interface IContainerSizeScanProvider : IContentProvider, IExitHandler
|
||||||
|
{
|
||||||
|
ISizeScanTask StartSizeScan(IContainer scanSizeOf);
|
||||||
|
}
|
||||||
@@ -1,17 +1,16 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using DeclarativeProperty;
|
using DeclarativeProperty;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using FileTime.Core.Models.ContainerTraits;
|
|
||||||
|
|
||||||
namespace FileTime.App.ContainerSizeScanner;
|
namespace FileTime.App.ContainerSizeScanner;
|
||||||
|
|
||||||
public interface ISizeScanContainer : ISizeItem, IContainer, IStatusProviderContainer
|
public interface ISizeScanContainer : ISizeItem, IContainer
|
||||||
{
|
{
|
||||||
public Task AddSizeSourceAsync(IDeclarativeProperty<long> sizeElement);
|
public Task AddSizeSourceAsync(IDeclarativeProperty<long> sizeElement);
|
||||||
ObservableCollection<ISizeScanContainer> ChildContainers { get; }
|
ObservableCollection<ISizeScanContainer> ChildContainers { get; }
|
||||||
ObservableCollection<ISizeScanElement> ChildElements { get; }
|
|
||||||
ObservableCollection<ISizeItem> SizeItems { get; }
|
ObservableCollection<ISizeItem> SizeItems { get; }
|
||||||
IContainer RealContainer { get; init; }
|
IContainer RealContainer { get; init; }
|
||||||
Task StartLoadingAsync();
|
Task StartLoadingAsync();
|
||||||
Task StopLoadingAsync();
|
Task StopLoadingAsync();
|
||||||
|
Task AddSizeChildAsync(ISizeItem newChild);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using FileTime.App.Core.Exceptions;
|
using FileTime.App.Core.Exceptions;
|
||||||
|
using FileTime.App.Core.Services;
|
||||||
using FileTime.Core.ContentAccess;
|
using FileTime.Core.ContentAccess;
|
||||||
using FileTime.Core.Enums;
|
using FileTime.Core.Enums;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
@@ -7,14 +8,14 @@ using InitableService;
|
|||||||
|
|
||||||
namespace FileTime.App.ContainerSizeScanner;
|
namespace FileTime.App.ContainerSizeScanner;
|
||||||
|
|
||||||
public class ContainerScanProvider : ContentProviderBase, IContainerScanSnapshotProvider
|
public class ContainerSizeSizeScanProvider : ContentProviderBase, IContainerSizeScanProvider
|
||||||
{
|
{
|
||||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly List<ISizeScanTask> _sizeScanTasks = new();
|
private readonly List<ISizeScanTask> _sizeScanTasks = new();
|
||||||
internal const string ContentProviderName = "container-size-scan";
|
internal const string ContentProviderName = "container-size-scan";
|
||||||
|
|
||||||
public ContainerScanProvider(
|
public ContainerSizeSizeScanProvider(
|
||||||
ITimelessContentProvider timelessContentProvider,
|
ITimelessContentProvider timelessContentProvider,
|
||||||
IServiceProvider serviceProvider)
|
IServiceProvider serviceProvider)
|
||||||
: base(ContentProviderName, timelessContentProvider)
|
: base(ContentProviderName, timelessContentProvider)
|
||||||
@@ -110,4 +111,20 @@ public class ContainerScanProvider : ContentProviderBase, IContainerScanSnapshot
|
|||||||
|
|
||||||
return searchTask;
|
return searchTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task ExitAsync(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
foreach (var sizeScanTask in _sizeScanTasks)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sizeScanTask.Stop();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,14 +5,15 @@ using DeclarativeProperty;
|
|||||||
using FileTime.Core.ContentAccess;
|
using FileTime.Core.ContentAccess;
|
||||||
using FileTime.Core.Enums;
|
using FileTime.Core.Enums;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using FileTime.Core.Models.ContainerTraits;
|
using FileTime.Core.Models.Extensions;
|
||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
namespace FileTime.App.ContainerSizeScanner;
|
namespace FileTime.App.ContainerSizeScanner;
|
||||||
|
|
||||||
//TODO: create readonly version
|
//TODO: create readonly version, or not...
|
||||||
public record SizeScanContainer : ISizeScanContainer, IEscHandlerContainer
|
public record SizeScanContainer : ISizeScanContainer
|
||||||
{
|
{
|
||||||
|
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||||
private readonly ReadOnlyExtensionCollection _readOnlyExtensions;
|
private readonly ReadOnlyExtensionCollection _readOnlyExtensions;
|
||||||
private readonly BehaviorSubject<bool> _isLoading = new(false);
|
private readonly BehaviorSubject<bool> _isLoading = new(false);
|
||||||
private readonly CombineProperty<long, long> _size;
|
private readonly CombineProperty<long, long> _size;
|
||||||
@@ -38,25 +39,35 @@ public record SizeScanContainer : ISizeScanContainer, IEscHandlerContainer
|
|||||||
public ObservableCollection<AbsolutePath> Items { get; } = new();
|
public ObservableCollection<AbsolutePath> Items { get; } = new();
|
||||||
public IObservable<bool> IsLoading { get; }
|
public IObservable<bool> IsLoading { get; }
|
||||||
public bool? IsLoaded { get; private set; }
|
public bool? IsLoaded { get; private set; }
|
||||||
public Task WaitForLoaded(CancellationToken token = default) => throw new NotImplementedException();
|
|
||||||
|
public async Task WaitForLoaded(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
while (IsLoaded != true) await Task.Delay(1, token);
|
||||||
|
}
|
||||||
|
|
||||||
public bool AllowRecursiveDeletion => false;
|
public bool AllowRecursiveDeletion => false;
|
||||||
|
|
||||||
public IDeclarativeProperty<long> Size { get; }
|
public IDeclarativeProperty<long> Size { get; }
|
||||||
|
|
||||||
public ObservableCollection<ISizeScanContainer> ChildContainers { get; } = new();
|
public ObservableCollection<ISizeScanContainer> ChildContainers { get; } = new();
|
||||||
public ObservableCollection<ISizeScanElement> ChildElements { get; } = new();
|
|
||||||
public ObservableCollection<ISizeItem> SizeItems { get; } = new();
|
public ObservableCollection<ISizeItem> SizeItems { get; } = new();
|
||||||
public required IContainer RealContainer { get; init; }
|
public required IContainer RealContainer { get; init; }
|
||||||
|
|
||||||
internal SizeScanContainer()
|
internal SizeScanContainer(ITimelessContentProvider timelessContentProvider)
|
||||||
{
|
{
|
||||||
|
_timelessContentProvider = timelessContentProvider;
|
||||||
_readOnlyExtensions = new ReadOnlyExtensionCollection(Extensions);
|
_readOnlyExtensions = new ReadOnlyExtensionCollection(Extensions);
|
||||||
|
|
||||||
IsLoading = _isLoading.AsObservable();
|
IsLoading = _isLoading.AsObservable();
|
||||||
|
CreatedAt = DateTime.Now;
|
||||||
|
|
||||||
_size = new(childContainerSizes => Task.FromResult(childContainerSizes.Sum()));
|
_size = new(childContainerSizes => Task.FromResult(childContainerSizes.Sum()));
|
||||||
Size = _size.Debounce(TimeSpan.FromSeconds(1));
|
Size = _size.Debounce(TimeSpan.FromSeconds(1));
|
||||||
CreatedAt = DateTime.Now;
|
|
||||||
|
Extensions.Add(new EscHandlerContainerExtension(HandleEsc));
|
||||||
|
Extensions.Add(new NonRestorableContainerExtension());
|
||||||
|
Extensions.Add(new RealContainerProviderExtension(() => new(_timelessContentProvider, RealContainer!)));
|
||||||
|
Extensions.Add(new StatusProviderContainerExtension(() => Status));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AddSizeSourceAsync(IDeclarativeProperty<long> sizeElement)
|
public async Task AddSizeSourceAsync(IDeclarativeProperty<long> sizeElement)
|
||||||
@@ -90,4 +101,19 @@ public record SizeScanContainer : ISizeScanContainer, IEscHandlerContainer
|
|||||||
SizeScanTask.Stop();
|
SizeScanTask.Stop();
|
||||||
return Task.FromResult(new ContainerEscapeResult(true));
|
return Task.FromResult(new ContainerEscapeResult(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task AddSizeChildAsync(ISizeItem newChild)
|
||||||
|
{
|
||||||
|
SizeItems.Add(newChild);
|
||||||
|
|
||||||
|
if (newChild is ISizeScanContainer newContainer)
|
||||||
|
{
|
||||||
|
ChildContainers.Add(newContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
await AddSizeSourceAsync(newChild.Size);
|
||||||
|
Items.Add(new AbsolutePath(
|
||||||
|
_timelessContentProvider,
|
||||||
|
newChild));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using DeclarativeProperty;
|
using DeclarativeProperty;
|
||||||
using FileTime.Core.Enums;
|
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using FileTime.Core.Models.Extensions;
|
using FileTime.Core.Models.Extensions;
|
||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
@@ -13,7 +12,7 @@ public class SizeScanTask : ISizeScanTask
|
|||||||
private int _processedItems;
|
private int _processedItems;
|
||||||
private ulong _processedItemsTotal;
|
private ulong _processedItemsTotal;
|
||||||
private IContainer _scanSizeOf = null!;
|
private IContainer _scanSizeOf = null!;
|
||||||
private readonly IContainerScanSnapshotProvider _containerScanSnapshotProvider;
|
private readonly IContainerSizeScanProvider _containerSizeScanProvider;
|
||||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||||
private readonly ILogger<SizeScanTask> _logger;
|
private readonly ILogger<SizeScanTask> _logger;
|
||||||
private Thread? _sizeScanThread;
|
private Thread? _sizeScanThread;
|
||||||
@@ -24,11 +23,11 @@ public class SizeScanTask : ISizeScanTask
|
|||||||
public bool IsRunning { get; private set; }
|
public bool IsRunning { get; private set; }
|
||||||
|
|
||||||
public SizeScanTask(
|
public SizeScanTask(
|
||||||
IContainerScanSnapshotProvider containerScanSnapshotProvider,
|
IContainerSizeScanProvider containerSizeScanProvider,
|
||||||
ITimelessContentProvider timelessContentProvider,
|
ITimelessContentProvider timelessContentProvider,
|
||||||
ILogger<SizeScanTask> logger)
|
ILogger<SizeScanTask> logger)
|
||||||
{
|
{
|
||||||
_containerScanSnapshotProvider = containerScanSnapshotProvider;
|
_containerSizeScanProvider = containerSizeScanProvider;
|
||||||
_timelessContentProvider = timelessContentProvider;
|
_timelessContentProvider = timelessContentProvider;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_containerStatusDebounced = _containerStatus.Debounce(TimeSpan.FromMilliseconds(250));
|
_containerStatusDebounced = _containerStatus.Debounce(TimeSpan.FromMilliseconds(250));
|
||||||
@@ -38,16 +37,16 @@ public class SizeScanTask : ISizeScanTask
|
|||||||
{
|
{
|
||||||
_scanSizeOf = scanSizeOf;
|
_scanSizeOf = scanSizeOf;
|
||||||
var name = $"{_searchId++}_{scanSizeOf.Name}";
|
var name = $"{_searchId++}_{scanSizeOf.Name}";
|
||||||
var randomId = ContainerScanProvider.ContentProviderName + Constants.SeparatorChar + name;
|
var randomId = ContainerSizeSizeScanProvider.ContentProviderName + Constants.SeparatorChar + name;
|
||||||
SizeSizeScanContainer = new SizeScanContainer
|
SizeSizeScanContainer = new SizeScanContainer(_timelessContentProvider)
|
||||||
{
|
{
|
||||||
Name = name,
|
Name = name,
|
||||||
DisplayName = scanSizeOf.DisplayName,
|
DisplayName = scanSizeOf.DisplayName,
|
||||||
FullName = new FullName(randomId),
|
FullName = new FullName(randomId),
|
||||||
NativePath = new NativePath(randomId),
|
NativePath = new NativePath(randomId),
|
||||||
Parent = new AbsolutePath(_timelessContentProvider, _containerScanSnapshotProvider),
|
Parent = new AbsolutePath(_timelessContentProvider, _containerSizeScanProvider),
|
||||||
RealContainer = scanSizeOf,
|
RealContainer = scanSizeOf,
|
||||||
Provider = _containerScanSnapshotProvider,
|
Provider = _containerSizeScanProvider,
|
||||||
Status = _containerStatusDebounced,
|
Status = _containerStatusDebounced,
|
||||||
SizeScanTask = this
|
SizeScanTask = this
|
||||||
};
|
};
|
||||||
@@ -102,30 +101,23 @@ public class SizeScanTask : ISizeScanTask
|
|||||||
if (_cancelled) return;
|
if (_cancelled) return;
|
||||||
|
|
||||||
var fileExtension = element.GetExtension<FileExtension>();
|
var fileExtension = element.GetExtension<FileExtension>();
|
||||||
if (fileExtension?.Size is not { } size) continue;
|
var size = fileExtension?.Size ?? 0;
|
||||||
|
|
||||||
var sizeProperty = new DeclarativeProperty<long>(size);
|
var sizeProperty = new DeclarativeProperty<long>(size);
|
||||||
|
|
||||||
var childName = sizeScanContainer.FullName!.GetChild(element.Name).Path;
|
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 childElement = new SizeScanElement
|
||||||
var childSearchContainer = new SizeScanElement
|
|
||||||
{
|
{
|
||||||
Name = element.Name,
|
Name = element.Name,
|
||||||
DisplayName = element.DisplayName,
|
DisplayName = element.DisplayName,
|
||||||
FullName = new FullName(childName),
|
FullName = new FullName(childName),
|
||||||
NativePath = new NativePath(childName),
|
NativePath = new NativePath(childName),
|
||||||
Parent = new AbsolutePath(_timelessContentProvider, sizeScanContainer),
|
Parent = new AbsolutePath(_timelessContentProvider, sizeScanContainer),
|
||||||
Provider = _containerScanSnapshotProvider,
|
Provider = _containerSizeScanProvider,
|
||||||
Size = sizeProperty
|
Size = sizeProperty
|
||||||
};
|
};
|
||||||
sizeScanContainer.SizeItems.Add(childSearchContainer);
|
await sizeScanContainer.AddSizeChildAsync(childElement);
|
||||||
|
|
||||||
_processedItems++;
|
_processedItems++;
|
||||||
_processedItemsTotal++;
|
_processedItemsTotal++;
|
||||||
}
|
}
|
||||||
@@ -135,7 +127,7 @@ public class SizeScanTask : ISizeScanTask
|
|||||||
if (_cancelled) return;
|
if (_cancelled) return;
|
||||||
|
|
||||||
var childName = sizeScanContainer.FullName!.GetChild(childContainer.Name).Path;
|
var childName = sizeScanContainer.FullName!.GetChild(childContainer.Name).Path;
|
||||||
var childSearchContainer = new SizeScanContainer
|
var childSearchContainer = new SizeScanContainer(_timelessContentProvider)
|
||||||
{
|
{
|
||||||
Name = childContainer.Name,
|
Name = childContainer.Name,
|
||||||
DisplayName = childContainer.DisplayName,
|
DisplayName = childContainer.DisplayName,
|
||||||
@@ -143,19 +135,12 @@ public class SizeScanTask : ISizeScanTask
|
|||||||
NativePath = new NativePath(childName),
|
NativePath = new NativePath(childName),
|
||||||
Parent = new AbsolutePath(_timelessContentProvider, sizeScanContainer),
|
Parent = new AbsolutePath(_timelessContentProvider, sizeScanContainer),
|
||||||
RealContainer = childContainer,
|
RealContainer = childContainer,
|
||||||
Provider = _containerScanSnapshotProvider,
|
Provider = _containerSizeScanProvider,
|
||||||
Status = _containerStatusDebounced,
|
Status = _containerStatusDebounced,
|
||||||
SizeScanTask = this
|
SizeScanTask = this
|
||||||
};
|
};
|
||||||
|
|
||||||
sizeScanContainer.ChildContainers.Add(childSearchContainer);
|
await sizeScanContainer.AddSizeChildAsync(childSearchContainer);
|
||||||
sizeScanContainer.SizeItems.Add(childSearchContainer);
|
|
||||||
await sizeScanContainer.AddSizeSourceAsync(childSearchContainer.Size);
|
|
||||||
sizeScanContainer.Items.Add(new AbsolutePath(
|
|
||||||
_timelessContentProvider,
|
|
||||||
PointInTime.Present,
|
|
||||||
new FullName(childName),
|
|
||||||
AbsolutePathType.Container));
|
|
||||||
|
|
||||||
await TraverseTree(childContainer, childSearchContainer);
|
await TraverseTree(childContainer, childSearchContainer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ public static class Startup
|
|||||||
{
|
{
|
||||||
public static IServiceCollection AddContainerSizeScanner(this IServiceCollection services)
|
public static IServiceCollection AddContainerSizeScanner(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.TryAddSingleton<IContainerScanSnapshotProvider, ContainerScanProvider>();
|
services.TryAddSingleton<IContainerSizeScanProvider, ContainerSizeSizeScanProvider>();
|
||||||
services.AddSingleton<IContentProvider>(sp => sp.GetRequiredService<IContainerScanSnapshotProvider>());
|
services.AddSingleton<IContentProvider>(sp => sp.GetRequiredService<IContainerSizeScanProvider>());
|
||||||
|
services.AddSingleton<IExitHandler>(sp => sp.GetRequiredService<IContainerSizeScanProvider>());
|
||||||
services.AddTransient<ISizeScanTask, SizeScanTask>();
|
services.AddTransient<ISizeScanTask, SizeScanTask>();
|
||||||
services.AddTransient<IItemPreviewProvider, PreviewProvider>();
|
services.AddTransient<IItemPreviewProvider, PreviewProvider>();
|
||||||
return services;
|
return services;
|
||||||
|
|||||||
@@ -8,25 +8,24 @@ public enum ItemNotFoundExceptionType
|
|||||||
FullName,
|
FullName,
|
||||||
NativePath
|
NativePath
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ItemNotFoundException : Exception
|
public class ItemNotFoundException : Exception
|
||||||
{
|
{
|
||||||
public string Path { get; }
|
public string Path { get; }
|
||||||
public ItemNotFoundExceptionType Type { get; } = ItemNotFoundExceptionType.Raw;
|
public ItemNotFoundExceptionType Type { get; } = ItemNotFoundExceptionType.Raw;
|
||||||
|
|
||||||
public ItemNotFoundException(string path)
|
public ItemNotFoundException(string path) : base("Item not found " + path)
|
||||||
{
|
{
|
||||||
Path = path;
|
Path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemNotFoundException(FullName path)
|
public ItemNotFoundException(FullName path) : this(path.Path)
|
||||||
{
|
{
|
||||||
Path = path.Path;
|
|
||||||
Type = ItemNotFoundExceptionType.FullName;
|
Type = ItemNotFoundExceptionType.FullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemNotFoundException(NativePath path)
|
public ItemNotFoundException(NativePath path) : this(path.Path)
|
||||||
{
|
{
|
||||||
Path = path.Path;
|
|
||||||
Type = ItemNotFoundExceptionType.NativePath;
|
Type = ItemNotFoundExceptionType.NativePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,4 +10,5 @@ public interface IModalService
|
|||||||
void OpenModal(IModalViewModel modalToOpen);
|
void OpenModal(IModalViewModel modalToOpen);
|
||||||
void CloseModal(IModalViewModel modalToClose);
|
void CloseModal(IModalViewModel modalToClose);
|
||||||
T OpenModal<T>() where T : IModalViewModel;
|
T OpenModal<T>() where T : IModalViewModel;
|
||||||
|
event EventHandler? AllModalClosed;
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ using System.Text.Json;
|
|||||||
using FileTime.App.Core.Models;
|
using FileTime.App.Core.Models;
|
||||||
using FileTime.App.Core.ViewModels;
|
using FileTime.App.Core.ViewModels;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Models.Extensions;
|
||||||
using FileTime.Core.Services;
|
using FileTime.Core.Services;
|
||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
using FileTime.Providers.Local;
|
using FileTime.Providers.Local;
|
||||||
@@ -202,7 +203,14 @@ public class TabPersistenceService : ITabPersistenceService
|
|||||||
{
|
{
|
||||||
var currentLocation = tab.CurrentLocation.Value;
|
var currentLocation = tab.CurrentLocation.Value;
|
||||||
if (currentLocation is null) continue;
|
if (currentLocation is null) continue;
|
||||||
tabStates.Add(new TabState(currentLocation.FullName!.Path, tab.TabNumber));
|
var path = currentLocation.FullName!.Path;
|
||||||
|
|
||||||
|
if (currentLocation.GetExtension<RealContainerProviderExtension>()?.RealContainer() is { } realPath)
|
||||||
|
{
|
||||||
|
path = realPath.Path.Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
tabStates.Add(new TabState(path, tab.TabNumber));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TabStates(
|
return new TabStates(
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using FileTime.Core.Services;
|
|||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
using FileTime.Providers.Local;
|
using FileTime.Providers.Local;
|
||||||
using InitableService;
|
using InitableService;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace FileTime.App.Core.Services.UserCommandHandler;
|
namespace FileTime.App.Core.Services.UserCommandHandler;
|
||||||
|
|
||||||
@@ -27,6 +28,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
private readonly IUserCommunicationService _userCommunicationService;
|
private readonly IUserCommunicationService _userCommunicationService;
|
||||||
private readonly IFrequencyNavigationService _frequencyNavigationService;
|
private readonly IFrequencyNavigationService _frequencyNavigationService;
|
||||||
private readonly ICommandPaletteService _commandPaletteService;
|
private readonly ICommandPaletteService _commandPaletteService;
|
||||||
|
private readonly ILogger<NavigationUserCommandHandlerService> _logger;
|
||||||
private ITabViewModel? _selectedTab;
|
private ITabViewModel? _selectedTab;
|
||||||
private IDeclarativeProperty<IContainer?>? _currentLocation;
|
private IDeclarativeProperty<IContainer?>? _currentLocation;
|
||||||
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
|
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
|
||||||
@@ -41,7 +43,8 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
ITimelessContentProvider timelessContentProvider,
|
ITimelessContentProvider timelessContentProvider,
|
||||||
IUserCommunicationService userCommunicationService,
|
IUserCommunicationService userCommunicationService,
|
||||||
IFrequencyNavigationService frequencyNavigationService,
|
IFrequencyNavigationService frequencyNavigationService,
|
||||||
ICommandPaletteService commandPaletteService) : base(appState)
|
ICommandPaletteService commandPaletteService,
|
||||||
|
ILogger<NavigationUserCommandHandlerService> logger) : base(appState)
|
||||||
{
|
{
|
||||||
_appState = appState;
|
_appState = appState;
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
@@ -51,6 +54,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
_userCommunicationService = userCommunicationService;
|
_userCommunicationService = userCommunicationService;
|
||||||
_frequencyNavigationService = frequencyNavigationService;
|
_frequencyNavigationService = frequencyNavigationService;
|
||||||
_commandPaletteService = commandPaletteService;
|
_commandPaletteService = commandPaletteService;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
SaveSelectedTab(t => _selectedTab = t);
|
SaveSelectedTab(t => _selectedTab = t);
|
||||||
SaveCurrentSelectedItem(i => _currentSelectedItem = i);
|
SaveCurrentSelectedItem(i => _currentSelectedItem = i);
|
||||||
@@ -332,11 +336,23 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
}
|
}
|
||||||
else if (tabViewModel == null)
|
else if (tabViewModel == null)
|
||||||
{
|
{
|
||||||
var newLocation = _currentLocation?.Value?.FullName is { } fullName
|
IContainer? newLocation = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
newLocation = _currentLocation?.Value?.FullName is { } fullName
|
||||||
? (IContainer) await _timelessContentProvider.GetItemByFullNameAsync(fullName, PointInTime.Present)
|
? (IContainer) await _timelessContentProvider.GetItemByFullNameAsync(fullName, PointInTime.Present)
|
||||||
: _localContentProvider;
|
: _localContentProvider;
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
var fullName = _currentLocation?.Value?.FullName?.Path ?? "unknown";
|
||||||
|
_logger.LogError(ex, "Could not resolve container while switching to tab {TabNumber} to path {FullName}", number, fullName);
|
||||||
|
}
|
||||||
|
|
||||||
var tab = await _serviceProvider.GetAsyncInitableResolver<IContainer>(newLocation)
|
newLocation ??= _localContentProvider;
|
||||||
|
|
||||||
|
var tab = await _serviceProvider.GetAsyncInitableResolver(newLocation)
|
||||||
.GetRequiredServiceAsync<ITab>();
|
.GetRequiredServiceAsync<ITab>();
|
||||||
var newTabViewModel = _serviceProvider.GetInitableResolver(tab, number).GetRequiredService<ITabViewModel>();
|
var newTabViewModel = _serviceProvider.GetInitableResolver(tab, number).GetRequiredService<ITabViewModel>();
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||||
private readonly IUserCommandHandlerService _userCommandHandlerService;
|
private readonly IUserCommandHandlerService _userCommandHandlerService;
|
||||||
private readonly IContentAccessorFactory _contentAccessorFactory;
|
private readonly IContentAccessorFactory _contentAccessorFactory;
|
||||||
private readonly IContainerScanSnapshotProvider _containerScanSnapshotProvider;
|
private readonly IContainerSizeScanProvider _containerSizeScanProvider;
|
||||||
private IDeclarativeProperty<IContainer?>? _currentLocation;
|
private IDeclarativeProperty<IContainer?>? _currentLocation;
|
||||||
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
|
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
|
||||||
private ITabViewModel? _currentSelectedTab;
|
private ITabViewModel? _currentSelectedTab;
|
||||||
@@ -36,7 +36,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
ITimelessContentProvider timelessContentProvider,
|
ITimelessContentProvider timelessContentProvider,
|
||||||
IUserCommandHandlerService userCommandHandlerService,
|
IUserCommandHandlerService userCommandHandlerService,
|
||||||
IContentAccessorFactory contentAccessorFactory,
|
IContentAccessorFactory contentAccessorFactory,
|
||||||
IContainerScanSnapshotProvider containerScanSnapshotProvider) : base(appState)
|
IContainerSizeScanProvider containerSizeScanProvider) : base(appState)
|
||||||
{
|
{
|
||||||
_systemClipboardService = systemClipboardService;
|
_systemClipboardService = systemClipboardService;
|
||||||
_userCommunicationService = userCommunicationService;
|
_userCommunicationService = userCommunicationService;
|
||||||
@@ -45,7 +45,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
_timelessContentProvider = timelessContentProvider;
|
_timelessContentProvider = timelessContentProvider;
|
||||||
_userCommandHandlerService = userCommandHandlerService;
|
_userCommandHandlerService = userCommandHandlerService;
|
||||||
_contentAccessorFactory = contentAccessorFactory;
|
_contentAccessorFactory = contentAccessorFactory;
|
||||||
_containerScanSnapshotProvider = containerScanSnapshotProvider;
|
_containerSizeScanProvider = containerSizeScanProvider;
|
||||||
SaveCurrentLocation(l => _currentLocation = l);
|
SaveCurrentLocation(l => _currentLocation = l);
|
||||||
SaveCurrentSelectedItem(i => _currentSelectedItem = i);
|
SaveCurrentSelectedItem(i => _currentSelectedItem = i);
|
||||||
SaveSelectedTab(t => _currentSelectedTab = t);
|
SaveSelectedTab(t => _currentSelectedTab = t);
|
||||||
@@ -65,7 +65,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
{
|
{
|
||||||
if (_currentLocation?.Value is null) return;
|
if (_currentLocation?.Value is null) return;
|
||||||
|
|
||||||
var searchTask = _containerScanSnapshotProvider.StartSizeScan(_currentLocation.Value);
|
var searchTask = _containerSizeScanProvider.StartSizeScan(_currentLocation.Value);
|
||||||
var openContainerCommand = new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, searchTask.SizeSizeScanContainer));
|
var openContainerCommand = new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, searchTask.SizeSizeScanContainer));
|
||||||
await _userCommandHandlerService.HandleCommandAsync(openContainerCommand);
|
await _userCommandHandlerService.HandleCommandAsync(openContainerCommand);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,9 @@ using System.Collections.ObjectModel;
|
|||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Reactive.Subjects;
|
using System.Reactive.Subjects;
|
||||||
using DeclarativeProperty;
|
using DeclarativeProperty;
|
||||||
using DynamicData;
|
|
||||||
using FileTime.App.Core.Models.Enums;
|
using FileTime.App.Core.Models.Enums;
|
||||||
using FileTime.App.Core.ViewModels.Timeline;
|
using FileTime.App.Core.ViewModels.Timeline;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models.Extensions;
|
||||||
using FileTime.Core.Models.ContainerTraits;
|
|
||||||
using MvvmGen;
|
using MvvmGen;
|
||||||
using MoreLinq;
|
using MoreLinq;
|
||||||
|
|
||||||
@@ -48,7 +46,7 @@ public abstract partial class AppStateBase : IAppState
|
|||||||
ContainerStatus = SelectedTab
|
ContainerStatus = SelectedTab
|
||||||
.Map(t => t?.CurrentLocation)
|
.Map(t => t?.CurrentLocation)
|
||||||
.Switch()
|
.Switch()
|
||||||
.Map(c => c is IStatusProviderContainer statusProvider ? statusProvider.Status : null)
|
.Map(c => c?.GetExtension<StatusProviderContainerExtension>()?.GetStatusProperty())
|
||||||
.Switch();
|
.Switch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using FileTime.App.Core.Services;
|
|||||||
using FileTime.App.FrequencyNavigation.Models;
|
using FileTime.App.FrequencyNavigation.Models;
|
||||||
using FileTime.App.FrequencyNavigation.ViewModels;
|
using FileTime.App.FrequencyNavigation.ViewModels;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Models.Extensions;
|
||||||
using FileTime.Core.Services;
|
using FileTime.Core.Services;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using PropertyChanged.SourceGenerator;
|
using PropertyChanged.SourceGenerator;
|
||||||
@@ -38,9 +39,16 @@ public partial class FrequencyNavigationService : IFrequencyNavigationService, I
|
|||||||
tabEvents.LocationChanged += OnTabLocationChanged;
|
tabEvents.LocationChanged += OnTabLocationChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnTabLocationChanged(object? sender, TabLocationChanged e)
|
async void OnTabLocationChanged(object? sender, TabLocationChanged e)
|
||||||
{
|
{
|
||||||
IncreaseContainerScore(e.Location);
|
try
|
||||||
|
{
|
||||||
|
await IncreaseContainerScore(e.Location);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Fatal error while increasing container score");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenNavigationWindow()
|
public void OpenNavigationWindow()
|
||||||
@@ -59,12 +67,16 @@ public partial class FrequencyNavigationService : IFrequencyNavigationService, I
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void IncreaseContainerScore(FullName containerName)
|
private async Task IncreaseContainerScore(IContainer container)
|
||||||
{
|
{
|
||||||
await _saveLock.WaitAsync();
|
await _saveLock.WaitAsync();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var containerNameString = containerName.Path;
|
if (container.GetExtension<NonRestorableContainerExtension>() is not null) return;
|
||||||
|
|
||||||
|
var containerNameString = container.FullName?.Path;
|
||||||
|
if (containerNameString is null) return;
|
||||||
|
|
||||||
if (_containerScores.ContainsKey(containerNameString))
|
if (_containerScores.ContainsKey(containerNameString))
|
||||||
{
|
{
|
||||||
_containerScores[containerNameString].Score++;
|
_containerScores[containerNameString].Score++;
|
||||||
@@ -89,6 +101,7 @@ public partial class FrequencyNavigationService : IFrequencyNavigationService, I
|
|||||||
if (TryAgeContainerScores() || DateTime.Now - _lastSave > TimeSpan.FromMinutes(5))
|
if (TryAgeContainerScores() || DateTime.Now - _lastSave > TimeSpan.FromMinutes(5))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: move to if above
|
//TODO: move to if above
|
||||||
await SaveStateAsync();
|
await SaveStateAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using FileTime.Core.Enums;
|
using FileTime.Core.Enums;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Models.Extensions;
|
||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
namespace FileTime.App.Search;
|
namespace FileTime.App.Search;
|
||||||
@@ -38,7 +39,8 @@ public class SearchTask : ISearchTask
|
|||||||
|
|
||||||
var extensions = new ExtensionCollection
|
var extensions = new ExtensionCollection
|
||||||
{
|
{
|
||||||
new SearchExtension(this)
|
new SearchExtension(this),
|
||||||
|
new RealContainerProviderExtension(() => new AbsolutePath(_timelessContentProvider, baseContainer))
|
||||||
};
|
};
|
||||||
_container = new Container(
|
_container = new Container(
|
||||||
baseContainer.Name,
|
baseContainer.Name,
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace FileTime.Core.Models.ContainerTraits;
|
|
||||||
|
|
||||||
public interface IEscHandlerContainer
|
|
||||||
{
|
|
||||||
Task<ContainerEscapeResult> HandleEsc();
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
using DeclarativeProperty;
|
|
||||||
|
|
||||||
namespace FileTime.Core.Models.ContainerTraits;
|
|
||||||
|
|
||||||
public interface IStatusProviderContainer
|
|
||||||
{
|
|
||||||
IDeclarativeProperty<string> Status { get; }
|
|
||||||
}
|
|
||||||
@@ -6,20 +6,12 @@ namespace FileTime.Core.Models;
|
|||||||
|
|
||||||
public class ExtensionCollection : IEnumerable<object>
|
public class ExtensionCollection : IEnumerable<object>
|
||||||
{
|
{
|
||||||
private readonly List<object> _extensions = new();
|
private readonly HashSet<object> _extensions = new();
|
||||||
|
|
||||||
public ExtensionCollection()
|
public ExtensionCollection()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExtensionCollection(IEnumerable<object> objects)
|
|
||||||
{
|
|
||||||
foreach (var obj in objects)
|
|
||||||
{
|
|
||||||
AddSafe(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddSafe(object obj)
|
private void AddSafe(object obj)
|
||||||
{
|
{
|
||||||
var objType = obj.GetType();
|
var objType = obj.GetType();
|
||||||
@@ -35,12 +27,7 @@ public class ExtensionCollection : IEnumerable<object>
|
|||||||
AddSafe(obj);
|
AddSafe(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove<T>()
|
public ReadOnlyExtensionCollection AsReadOnly() => new(this);
|
||||||
{
|
|
||||||
_extensions.RemoveAll(i => i is T);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyExtensionCollection AsReadOnly() => new ReadOnlyExtensionCollection(this);
|
|
||||||
|
|
||||||
public IEnumerator<object> GetEnumerator() => _extensions.GetEnumerator();
|
public IEnumerator<object> GetEnumerator() => _extensions.GetEnumerator();
|
||||||
IEnumerator IEnumerable.GetEnumerator() => _extensions.GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => _extensions.GetEnumerator();
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace FileTime.Core.Models.Extensions;
|
||||||
|
|
||||||
|
public class EscHandlerContainerExtension
|
||||||
|
{
|
||||||
|
private readonly Func<Task<ContainerEscapeResult>> _handleEsc;
|
||||||
|
|
||||||
|
public EscHandlerContainerExtension(Func<Task<ContainerEscapeResult>> handleEsc)
|
||||||
|
{
|
||||||
|
_handleEsc = handleEsc;
|
||||||
|
}
|
||||||
|
public async Task<ContainerEscapeResult> HandleEsc() => await _handleEsc();
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace FileTime.Core.Models.Extensions;
|
||||||
|
|
||||||
|
public class NonRestorableContainerExtension
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
namespace FileTime.Core.Models.Extensions;
|
||||||
|
|
||||||
|
public class RealContainerProviderExtension
|
||||||
|
{
|
||||||
|
public RealContainerProviderExtension(Func<AbsolutePath> realContainer)
|
||||||
|
{
|
||||||
|
RealContainer = realContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Func<AbsolutePath> RealContainer { get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using DeclarativeProperty;
|
||||||
|
|
||||||
|
namespace FileTime.Core.Models.Extensions;
|
||||||
|
|
||||||
|
public class StatusProviderContainerExtension
|
||||||
|
{
|
||||||
|
public StatusProviderContainerExtension(Func<IDeclarativeProperty<string>> getStatusProperty)
|
||||||
|
{
|
||||||
|
GetStatusProperty = getStatusProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Func<IDeclarativeProperty<string>> GetStatusProperty { get; }
|
||||||
|
}
|
||||||
@@ -4,10 +4,10 @@ namespace FileTime.Core.Models;
|
|||||||
|
|
||||||
public class TabLocationChanged : EventArgs
|
public class TabLocationChanged : EventArgs
|
||||||
{
|
{
|
||||||
public FullName Location { get; }
|
public IContainer Location { get; }
|
||||||
public ITab Tab { get; }
|
public ITab Tab { get; }
|
||||||
|
|
||||||
public TabLocationChanged(FullName location, ITab tab)
|
public TabLocationChanged(IContainer location, ITab tab)
|
||||||
{
|
{
|
||||||
Location = location;
|
Location = location;
|
||||||
Tab = tab;
|
Tab = tab;
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ namespace FileTime.Core.Services;
|
|||||||
public interface ITabEvents
|
public interface ITabEvents
|
||||||
{
|
{
|
||||||
event EventHandler<TabLocationChanged> LocationChanged;
|
event EventHandler<TabLocationChanged> LocationChanged;
|
||||||
void OnLocationChanged(ITab tab, FullName location);
|
void OnLocationChanged(ITab tab, IContainer location);
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,7 @@ public class CopyCommand : CommandBase, ITransportationCommand
|
|||||||
{
|
{
|
||||||
var elapsed = DateTime.Now - start;
|
var elapsed = DateTime.Now - start;
|
||||||
|
|
||||||
var size = new ByteSize(total / elapsed.TotalSeconds);
|
var size = ByteSize.FromBytes(total / elapsed.TotalSeconds);
|
||||||
return Task.FromResult(size + "/s");
|
return Task.FromResult(size + "/s");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ public class Tab : ITab
|
|||||||
|
|
||||||
if (newLocation.FullName != null)
|
if (newLocation.FullName != null)
|
||||||
{
|
{
|
||||||
_tabEvents.OnLocationChanged(this, newLocation.FullName);
|
_tabEvents.OnLocationChanged(this, newLocation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ public class Tab : ITab
|
|||||||
|
|
||||||
if (newLocation.FullName != null)
|
if (newLocation.FullName != null)
|
||||||
{
|
{
|
||||||
_tabEvents.OnLocationChanged(this, newLocation.FullName);
|
_tabEvents.OnLocationChanged(this, newLocation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,8 @@ namespace FileTime.Core.Services;
|
|||||||
|
|
||||||
public class TabEvents : ITabEvents
|
public class TabEvents : ITabEvents
|
||||||
{
|
{
|
||||||
public event EventHandler<TabLocationChanged> LocationChanged;
|
public event EventHandler<TabLocationChanged>? LocationChanged;
|
||||||
|
|
||||||
public void OnLocationChanged(ITab tab, FullName location)
|
public void OnLocationChanged(ITab tab, IContainer location)
|
||||||
{
|
=> LocationChanged?.Invoke(this, new TabLocationChanged(location, tab));
|
||||||
LocationChanged?.Invoke(this, new TabLocationChanged(location, tab));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -22,8 +22,9 @@ public class FormatSizeConverter : IValueConverter
|
|||||||
|
|
||||||
public static string ToSizeString(long fileSize, int? precision = null)
|
public static string ToSizeString(long fileSize, int? precision = null)
|
||||||
{
|
{
|
||||||
var size = new ByteSize(fileSize);
|
var size = ByteSize.FromBytes(fileSize);
|
||||||
return precision == null? size.ToString()
|
return precision == null
|
||||||
|
? size.ToString()
|
||||||
: size.ToString("0." + new string('#', precision.Value));
|
: size.ToString("0." + new string('#', precision.Value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
using System.Reactive.Linq;
|
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using FileTime.App.Core.Services;
|
using FileTime.App.Core.Services;
|
||||||
using FileTime.App.Core.UserCommand;
|
using FileTime.App.Core.UserCommand;
|
||||||
using FileTime.App.Core.ViewModels;
|
using FileTime.App.Core.ViewModels;
|
||||||
using FileTime.Core.Extensions;
|
using FileTime.Core.Extensions;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using FileTime.Core.Models.ContainerTraits;
|
using FileTime.Core.Models.Extensions;
|
||||||
using FileTime.GuiApp.Configuration;
|
using FileTime.GuiApp.Configuration;
|
||||||
using FileTime.GuiApp.Extensions;
|
using FileTime.GuiApp.Extensions;
|
||||||
using FileTime.GuiApp.Models;
|
using FileTime.GuiApp.Models;
|
||||||
using FileTime.GuiApp.ViewModels;
|
using FileTime.GuiApp.ViewModels;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using DeclarativeProperty;
|
||||||
|
|
||||||
namespace FileTime.GuiApp.Services;
|
namespace FileTime.GuiApp.Services;
|
||||||
|
|
||||||
@@ -20,8 +20,7 @@ public class DefaultModeKeyInputHandler : IDefaultModeKeyInputHandler
|
|||||||
private readonly IModalService _modalService;
|
private readonly IModalService _modalService;
|
||||||
private readonly IKeyboardConfigurationService _keyboardConfigurationService;
|
private readonly IKeyboardConfigurationService _keyboardConfigurationService;
|
||||||
private readonly List<KeyConfig[]> _keysToSkip = new();
|
private readonly List<KeyConfig[]> _keysToSkip = new();
|
||||||
private ITabViewModel? _selectedTab;
|
private readonly IDeclarativeProperty<IContainer?> _currentLocation;
|
||||||
private IContainer? _currentLocation;
|
|
||||||
private readonly ILogger<DefaultModeKeyInputHandler> _logger;
|
private readonly ILogger<DefaultModeKeyInputHandler> _logger;
|
||||||
private readonly IUserCommandHandlerService _userCommandHandlerService;
|
private readonly IUserCommandHandlerService _userCommandHandlerService;
|
||||||
private readonly IIdentifiableUserCommandService _identifiableUserCommandService;
|
private readonly IIdentifiableUserCommandService _identifiableUserCommandService;
|
||||||
@@ -42,19 +41,20 @@ public class DefaultModeKeyInputHandler : IDefaultModeKeyInputHandler
|
|||||||
_modalService = modalService;
|
_modalService = modalService;
|
||||||
_userCommandHandlerService = userCommandHandlerService;
|
_userCommandHandlerService = userCommandHandlerService;
|
||||||
|
|
||||||
_appState.SelectedTab.Subscribe(t => _selectedTab = t);
|
_currentLocation = _appState.SelectedTab
|
||||||
_appState.SelectedTab.Select(t => t == null ? Observable.Return<IContainer?>(null) : t.CurrentLocation!).Switch().Subscribe(l => _currentLocation = l);
|
.Map(t => t?.CurrentLocation)
|
||||||
|
.Switch();
|
||||||
|
|
||||||
_openModals = modalService.OpenModals.ToBindedCollection();
|
_openModals = modalService.OpenModals.ToBindedCollection();
|
||||||
|
|
||||||
_keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.Up) });
|
_keysToSkip.Add(new[] {new KeyConfig(Key.Up)});
|
||||||
_keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.Down) });
|
_keysToSkip.Add(new[] {new KeyConfig(Key.Down)});
|
||||||
_keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.Tab) });
|
_keysToSkip.Add(new[] {new KeyConfig(Key.Tab)});
|
||||||
_keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.PageDown) });
|
_keysToSkip.Add(new[] {new KeyConfig(Key.PageDown)});
|
||||||
_keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.PageUp) });
|
_keysToSkip.Add(new[] {new KeyConfig(Key.PageUp)});
|
||||||
_keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.F4, alt: true) });
|
_keysToSkip.Add(new[] {new KeyConfig(Key.F4, alt: true)});
|
||||||
_keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.LWin) });
|
_keysToSkip.Add(new[] {new KeyConfig(Key.LWin)});
|
||||||
_keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.RWin) });
|
_keysToSkip.Add(new[] {new KeyConfig(Key.RWin)});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task HandleInputKey(Key key, SpecialKeysStatus specialKeysStatus, Action<bool> setHandled)
|
public async Task HandleInputKey(Key key, SpecialKeysStatus specialKeysStatus, Action<bool> setHandled)
|
||||||
@@ -73,7 +73,7 @@ public class DefaultModeKeyInputHandler : IDefaultModeKeyInputHandler
|
|||||||
{
|
{
|
||||||
_modalService.CloseModal(_openModals.Collection!.Last());
|
_modalService.CloseModal(_openModals.Collection!.Last());
|
||||||
}
|
}
|
||||||
else if (_currentLocation is IEscHandlerContainer escHandler)
|
else if (_currentLocation.Value?.GetExtension<EscHandlerContainerExtension>() is { } escHandler)
|
||||||
{
|
{
|
||||||
var escapeResult = await escHandler.HandleEsc();
|
var escapeResult = await escHandler.HandleEsc();
|
||||||
if (escapeResult.NavigateTo != null)
|
if (escapeResult.NavigateTo != null)
|
||||||
@@ -118,7 +118,11 @@ public class DefaultModeKeyInputHandler : IDefaultModeKeyInputHandler
|
|||||||
setHandled(true);
|
setHandled(true);
|
||||||
_appState.PreviousKeys.Clear();
|
_appState.PreviousKeys.Clear();
|
||||||
_appState.PossibleCommands = new();
|
_appState.PossibleCommands = new();
|
||||||
await CallCommandAsync(_identifiableUserCommandService.GetCommand(selectedCommandBinding.Command));
|
var command = _identifiableUserCommandService.GetCommand(selectedCommandBinding.Command);
|
||||||
|
if (command is not null)
|
||||||
|
{
|
||||||
|
await CallCommandAsync(command);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (_keysToSkip.Any(k => k.AreKeysEqual(_appState.PreviousKeys)))
|
else if (_keysToSkip.Any(k => k.AreKeysEqual(_appState.PreviousKeys)))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ public class ModalService : IModalService
|
|||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly SourceList<IModalViewModel> _openModals = new();
|
private readonly SourceList<IModalViewModel> _openModals = new();
|
||||||
public IObservable<IChangeSet<IModalViewModel>> OpenModals { get; }
|
public IObservable<IChangeSet<IModalViewModel>> OpenModals { get; }
|
||||||
|
public event EventHandler? AllModalClosed;
|
||||||
|
|
||||||
public ModalService(IServiceProvider serviceProvider)
|
public ModalService(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
@@ -19,7 +20,14 @@ public class ModalService : IModalService
|
|||||||
|
|
||||||
public void OpenModal(IModalViewModel modalToOpen) => _openModals.Add(modalToOpen);
|
public void OpenModal(IModalViewModel modalToOpen) => _openModals.Add(modalToOpen);
|
||||||
|
|
||||||
public void CloseModal(IModalViewModel modalToClose) => _openModals.Remove(modalToClose);
|
public void CloseModal(IModalViewModel modalToClose)
|
||||||
|
{
|
||||||
|
_openModals.Remove(modalToClose);
|
||||||
|
if (_openModals.Count == 0)
|
||||||
|
{
|
||||||
|
AllModalClosed?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public T OpenModal<T>() where T : IModalViewModel
|
public T OpenModal<T>() where T : IModalViewModel
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,12 +32,14 @@ namespace FileTime.GuiApp.ViewModels;
|
|||||||
[Inject(typeof(IRefreshSmoothnessCalculator), PropertyAccessModifier = AccessModifier.Public)]
|
[Inject(typeof(IRefreshSmoothnessCalculator), PropertyAccessModifier = AccessModifier.Public)]
|
||||||
[Inject(typeof(IAdminElevationManager), PropertyAccessModifier = AccessModifier.Public)]
|
[Inject(typeof(IAdminElevationManager), PropertyAccessModifier = AccessModifier.Public)]
|
||||||
[Inject(typeof(IClipboardService), PropertyAccessModifier = AccessModifier.Public)]
|
[Inject(typeof(IClipboardService), PropertyAccessModifier = AccessModifier.Public)]
|
||||||
|
[Inject(typeof(IModalService), PropertyName = "_modalService")]
|
||||||
public partial class MainWindowViewModel : IMainWindowViewModel
|
public partial class MainWindowViewModel : IMainWindowViewModel
|
||||||
{
|
{
|
||||||
public bool Loading => false;
|
public bool Loading => false;
|
||||||
public IObservable<string?> MainFont => _fontService.MainFont.Select(x => x ?? "");
|
public IObservable<string?> MainFont => _fontService.MainFont.Select(x => x ?? "");
|
||||||
public IGuiAppState AppState => _appState;
|
public IGuiAppState AppState => _appState;
|
||||||
public string Title { get; private set; }
|
public string Title { get; private set; }
|
||||||
|
public Action? FocusDefaultElement { get; set; }
|
||||||
|
|
||||||
partial void OnInitialize()
|
partial void OnInitialize()
|
||||||
{
|
{
|
||||||
@@ -59,13 +61,13 @@ public partial class MainWindowViewModel : IMainWindowViewModel
|
|||||||
Title += " (Debug)";
|
Title += " (Debug)";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
_modalService.AllModalClosed += (_, _) => FocusDefaultElement?.Invoke();
|
||||||
|
|
||||||
Task.Run(async () => await _lifecycleService.InitStartupHandlersAsync()).Wait();
|
Task.Run(async () => await _lifecycleService.InitStartupHandlersAsync()).Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ProcessKeyDown(Key key, KeyModifiers keyModifiers, Action<bool> setHandled)
|
public void ProcessKeyDown(Key key, KeyModifiers keyModifiers, Action<bool> setHandled)
|
||||||
{
|
=> _keyInputHandlerService.ProcessKeyDown(key, keyModifiers, setHandled);
|
||||||
_keyInputHandlerService.ProcessKeyDown(key, keyModifiers, setHandled);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task OpenContainerByFullName(FullName fullName)
|
public async Task OpenContainerByFullName(FullName fullName)
|
||||||
{
|
{
|
||||||
@@ -76,7 +78,5 @@ public partial class MainWindowViewModel : IMainWindowViewModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnExit()
|
public async Task OnExit()
|
||||||
{
|
=> await _lifecycleService.ExitAsync();
|
||||||
await _lifecycleService.ExitAsync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,10 @@ public partial class MainWindow : Window, IUiAccessor
|
|||||||
|
|
||||||
_logger?.LogInformation(
|
_logger?.LogInformation(
|
||||||
$"{nameof(MainWindow)} opened, starting {nameof(MainWindowViewModel)} initialization...");
|
$"{nameof(MainWindow)} opened, starting {nameof(MainWindowViewModel)} initialization...");
|
||||||
ViewModel = DI.ServiceProvider.GetRequiredService<MainWindowViewModel>();
|
|
||||||
|
var viewModel = DI.ServiceProvider.GetRequiredService<MainWindowViewModel>();
|
||||||
|
viewModel.FocusDefaultElement = () => Focus();
|
||||||
|
ViewModel = viewModel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +157,12 @@ public partial class MainWindow : Window, IUiAccessor
|
|||||||
private void OnWindowClosed(object? sender, EventArgs e)
|
private void OnWindowClosed(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var vm = ViewModel;
|
var vm = ViewModel;
|
||||||
Task.Run(() => vm?.OnExit()).Wait();
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
if (vm is null) return;
|
||||||
|
await vm.OnExit();
|
||||||
|
})
|
||||||
|
.Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InputList_OnKeyUp(object? sender, KeyEventArgs e)
|
private void InputList_OnKeyUp(object? sender, KeyEventArgs e)
|
||||||
|
|||||||
Reference in New Issue
Block a user