Element preview
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
namespace FileTime.App.Core.Models;
|
||||||
|
|
||||||
|
public record FileExtension(long? Size);
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace FileTime.App.Core.Models;
|
||||||
|
|
||||||
|
public enum ItemPreviewMode
|
||||||
|
{
|
||||||
|
Unknown,
|
||||||
|
Text,
|
||||||
|
Empty
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||||
|
|
||||||
|
namespace FileTime.App.Core.Services;
|
||||||
|
|
||||||
|
public interface IItemPreviewService
|
||||||
|
{
|
||||||
|
IObservable<IItemPreviewViewModel?> ItemPreview { get; }
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
|
using FileTime.App.Core.Models;
|
||||||
using FileTime.App.Core.Models.Enums;
|
using FileTime.App.Core.Models.Enums;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using InitableService;
|
using InitableService;
|
||||||
|
|
||||||
namespace FileTime.App.Core.ViewModels;
|
namespace FileTime.App.Core.ViewModels;
|
||||||
|
|
||||||
public interface IFileViewModel : IElementViewModel, IInitable<IFileElement, ITabViewModel, ItemViewModelType>
|
public interface IFileViewModel : IElementViewModel, IInitable<IElement, FileExtension, ITabViewModel, ItemViewModelType>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using FileTime.App.Core.Models;
|
||||||
|
|
||||||
|
namespace FileTime.App.Core.ViewModels.ItemPreview;
|
||||||
|
|
||||||
|
public interface IItemPreviewViewModel
|
||||||
|
{
|
||||||
|
ItemPreviewMode Mode { get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using System.Reactive.Linq;
|
||||||
|
using FileTime.App.Core.ViewModels;
|
||||||
|
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace FileTime.App.Core.Services;
|
||||||
|
|
||||||
|
public class ItemPreviewService : IItemPreviewService
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
public IObservable<IItemPreviewViewModel?> ItemPreview { get; }
|
||||||
|
|
||||||
|
public ItemPreviewService(IAppState appState, IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
ItemPreview = appState
|
||||||
|
.SelectedTab
|
||||||
|
.Select(t => t?.CurrentSelectedItem.Throttle(TimeSpan.FromMilliseconds(250)) ?? Observable.Return<IItemViewModel?>(null))
|
||||||
|
.Switch()
|
||||||
|
.Select(item =>
|
||||||
|
item == null
|
||||||
|
? Observable.Return<IItemPreviewViewModel?>(null)
|
||||||
|
: Observable.FromAsync(async () => await Map(item))
|
||||||
|
)
|
||||||
|
.Switch()
|
||||||
|
.Publish(null)
|
||||||
|
.RefCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IItemPreviewViewModel?> Map(IItemViewModel itemViewModel)
|
||||||
|
{
|
||||||
|
return itemViewModel.BaseItem switch
|
||||||
|
{
|
||||||
|
IElement element => await _serviceProvider.GetAsyncInitableResolver(element)
|
||||||
|
.GetRequiredServiceAsync<ElementPreviewViewModel>(),
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ using FileTime.App.Core.Services;
|
|||||||
using FileTime.App.Core.Services.UserCommandHandler;
|
using FileTime.App.Core.Services.UserCommandHandler;
|
||||||
using FileTime.App.Core.StartupServices;
|
using FileTime.App.Core.StartupServices;
|
||||||
using FileTime.App.Core.ViewModels;
|
using FileTime.App.Core.ViewModels;
|
||||||
|
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace FileTime.App.Core;
|
namespace FileTime.App.Core;
|
||||||
@@ -17,10 +18,12 @@ public static class Startup
|
|||||||
.AddTransient<IFileViewModel, FileViewModel>()
|
.AddTransient<IFileViewModel, FileViewModel>()
|
||||||
.AddTransient<IContainerSizeContainerViewModel, ContainerSizeContainerViewModel>()
|
.AddTransient<IContainerSizeContainerViewModel, ContainerSizeContainerViewModel>()
|
||||||
.AddTransient<IItemNameConverterService, ItemNameConverterService>()
|
.AddTransient<IItemNameConverterService, ItemNameConverterService>()
|
||||||
|
.AddTransient<ElementPreviewViewModel>()
|
||||||
.AddSingleton<IUserCommandHandlerService, UserCommandHandlerService>()
|
.AddSingleton<IUserCommandHandlerService, UserCommandHandlerService>()
|
||||||
.AddSingleton<IClipboardService, ClipboardService>()
|
.AddSingleton<IClipboardService, ClipboardService>()
|
||||||
.AddSingleton<IIdentifiableUserCommandService, IdentifiableUserCommandService>()
|
.AddSingleton<IIdentifiableUserCommandService, IdentifiableUserCommandService>()
|
||||||
.AddSingleton<IStartupHandler, DefaultIdentifiableCommandHandlerRegister>()
|
.AddSingleton<IStartupHandler, DefaultIdentifiableCommandHandlerRegister>()
|
||||||
|
.AddSingleton<IItemPreviewService, ItemPreviewService>()
|
||||||
.AddCommandHandlers();
|
.AddCommandHandlers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using FileTime.App.Core.Models;
|
||||||
using FileTime.App.Core.Models.Enums;
|
using FileTime.App.Core.Models.Enums;
|
||||||
using FileTime.App.Core.Services;
|
using FileTime.App.Core.Services;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
@@ -12,8 +13,9 @@ public partial class FileViewModel : ElementViewModel, IFileViewModel
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Init(IFileElement item, ITabViewModel parentTab, ItemViewModelType itemViewModelType)
|
public void Init(IElement item, FileExtension fileExtension, ITabViewModel parentTab, ItemViewModelType itemViewModelType)
|
||||||
{
|
{
|
||||||
Init((IElement)item, parentTab, itemViewModelType);
|
Init((IElement)item, parentTab, itemViewModelType);
|
||||||
|
Size = fileExtension.Size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using System.Text;
|
||||||
|
using FileTime.App.Core.Models;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
using InitableService;
|
||||||
|
using MvvmGen;
|
||||||
|
|
||||||
|
namespace FileTime.App.Core.ViewModels.ItemPreview;
|
||||||
|
|
||||||
|
[ViewModel]
|
||||||
|
public partial class ElementPreviewViewModel : IItemPreviewViewModel, IAsyncInitable<IElement>
|
||||||
|
{
|
||||||
|
private const int MaxTextPreviewSize = 1024 * 1024;
|
||||||
|
|
||||||
|
public ItemPreviewMode Mode { get; private set; }
|
||||||
|
|
||||||
|
[Property] private string? _textContent;
|
||||||
|
|
||||||
|
public async Task InitAsync(IElement element)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var content = await element.Provider.GetContentAsync(element, MaxTextPreviewSize);
|
||||||
|
|
||||||
|
TextContent = content is null
|
||||||
|
? "Could not read any data from file " + element.Name
|
||||||
|
: Encoding.UTF8.GetString(content);
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
TextContent = $"Error while getting content of {element.FullName}. " + ex.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Mode = (TextContent?.Length ?? 0) switch
|
||||||
|
{
|
||||||
|
0 => ItemPreviewMode.Empty,
|
||||||
|
_ => ItemPreviewMode.Text
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ using FileTime.App.Core.Extensions;
|
|||||||
using FileTime.App.Core.Models;
|
using FileTime.App.Core.Models;
|
||||||
using FileTime.App.Core.Models.Enums;
|
using FileTime.App.Core.Models.Enums;
|
||||||
using FileTime.App.Core.Services;
|
using FileTime.App.Core.Services;
|
||||||
|
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using FileTime.Core.Services;
|
using FileTime.Core.Services;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@@ -34,9 +35,14 @@ public partial class TabViewModel : ITabViewModel, IDisposable
|
|||||||
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> SelectedsChildren { get; private set; } = null!;
|
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> SelectedsChildren { get; private set; } = null!;
|
||||||
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> ParentsChildren { get; private set; } = null!;
|
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> ParentsChildren { get; private set; } = null!;
|
||||||
|
|
||||||
public IObservable<IReadOnlyCollection<IItemViewModel>?> CurrentItemsCollectionObservable { get; private set; } = null!;
|
public IObservable<IReadOnlyCollection<IItemViewModel>?> CurrentItemsCollectionObservable { get; private set; } =
|
||||||
public IObservable<IReadOnlyCollection<IItemViewModel>?> ParentsChildrenCollectionObservable { get; private set; } = null!;
|
null!;
|
||||||
public IObservable<IReadOnlyCollection<IItemViewModel>?> SelectedsChildrenCollectionObservable { get; private set; } = null!;
|
|
||||||
|
public IObservable<IReadOnlyCollection<IItemViewModel>?> ParentsChildrenCollectionObservable { get; private set; } =
|
||||||
|
null!;
|
||||||
|
|
||||||
|
public IObservable<IReadOnlyCollection<IItemViewModel>?>
|
||||||
|
SelectedsChildrenCollectionObservable { get; private set; } = null!;
|
||||||
|
|
||||||
[Property] private BindedCollection<IItemViewModel>? _currentItemsCollection;
|
[Property] private BindedCollection<IItemViewModel>? _currentItemsCollection;
|
||||||
|
|
||||||
@@ -78,7 +84,7 @@ public partial class TabViewModel : ITabViewModel, IDisposable
|
|||||||
tab.CurrentSelectedItem,
|
tab.CurrentSelectedItem,
|
||||||
(currentItems, currentSelectedItemPath) =>
|
(currentItems, currentSelectedItemPath) =>
|
||||||
currentItems == null
|
currentItems == null
|
||||||
? Observable.Return((IItemViewModel?) null)
|
? Observable.Return((IItemViewModel?)null)
|
||||||
: currentItems
|
: currentItems
|
||||||
.ToCollection()
|
.ToCollection()
|
||||||
.Select(items =>
|
.Select(items =>
|
||||||
@@ -117,7 +123,7 @@ public partial class TabViewModel : ITabViewModel, IDisposable
|
|||||||
.Transform(i => MapItemToViewModel(i, ItemViewModelType.SelectedChild))),
|
.Transform(i => MapItemToViewModel(i, ItemViewModelType.SelectedChild))),
|
||||||
currentSelectedItemThrottled
|
currentSelectedItemThrottled
|
||||||
.Where(c => c is null or not IContainerViewModel)
|
.Where(c => c is null or not IContainerViewModel)
|
||||||
.Select(_ => (IObservable<IChangeSet<IItemViewModel>>?) null)
|
.Select(_ => (IObservable<IChangeSet<IItemViewModel>>?)null)
|
||||||
)
|
)
|
||||||
/*.ObserveOn(_rxSchedulerService.GetWorkerScheduler())
|
/*.ObserveOn(_rxSchedulerService.GetWorkerScheduler())
|
||||||
.SubscribeOn(_rxSchedulerService.GetUIScheduler())*/
|
.SubscribeOn(_rxSchedulerService.GetUIScheduler())*/
|
||||||
@@ -136,7 +142,7 @@ public partial class TabViewModel : ITabViewModel, IDisposable
|
|||||||
return Observable.Merge(
|
return Observable.Merge(
|
||||||
parentThrottled
|
parentThrottled
|
||||||
.Where(p => p is not null)
|
.Where(p => p is not null)
|
||||||
.Select(p => Observable.FromAsync(async () => (IContainer) await p!.ResolveAsync()))
|
.Select(p => Observable.FromAsync(async () => (IContainer)await p!.ResolveAsync()))
|
||||||
.Switch()
|
.Switch()
|
||||||
.Select(p => p.Items)
|
.Select(p => p.Items)
|
||||||
.Switch()
|
.Switch()
|
||||||
@@ -145,7 +151,7 @@ public partial class TabViewModel : ITabViewModel, IDisposable
|
|||||||
.Transform(i => MapItemToViewModel(i, ItemViewModelType.Parent))),
|
.Transform(i => MapItemToViewModel(i, ItemViewModelType.Parent))),
|
||||||
parentThrottled
|
parentThrottled
|
||||||
.Where(p => p is null)
|
.Where(p => p is null)
|
||||||
.Select(_ => (IObservable<IChangeSet<IItemViewModel>>?) null)
|
.Select(_ => (IObservable<IChangeSet<IItemViewModel>>?)null)
|
||||||
)
|
)
|
||||||
/*.ObserveOn(_rxSchedulerService.GetWorkerScheduler())
|
/*.ObserveOn(_rxSchedulerService.GetWorkerScheduler())
|
||||||
.SubscribeOn(_rxSchedulerService.GetUIScheduler())*/
|
.SubscribeOn(_rxSchedulerService.GetUIScheduler())*/
|
||||||
@@ -158,7 +164,7 @@ public partial class TabViewModel : ITabViewModel, IDisposable
|
|||||||
{
|
{
|
||||||
return source
|
return source
|
||||||
.Select(c =>
|
.Select(c =>
|
||||||
c != null ? c.ToCollection() : Observable.Return((IReadOnlyCollection<IItemViewModel>?) null))
|
c != null ? c.ToCollection() : Observable.Return((IReadOnlyCollection<IItemViewModel>?)null))
|
||||||
.Switch()
|
.Switch()
|
||||||
.Publish(null)
|
.Publish(null)
|
||||||
.RefCount();
|
.RefCount();
|
||||||
@@ -179,22 +185,27 @@ public partial class TabViewModel : ITabViewModel, IDisposable
|
|||||||
|
|
||||||
return containerViewModel;
|
return containerViewModel;
|
||||||
}
|
}
|
||||||
else if (item is IFileElement fileElement)
|
|
||||||
{
|
|
||||||
var fileViewModel = _serviceProvider
|
|
||||||
.GetInitableResolver<IFileElement, ITabViewModel, ItemViewModelType>(fileElement, this, type)
|
|
||||||
.GetRequiredService<IFileViewModel>();
|
|
||||||
fileViewModel.Size = fileElement.Size;
|
|
||||||
|
|
||||||
return fileViewModel;
|
|
||||||
}
|
|
||||||
else if (item is IElement element)
|
else if (item is IElement element)
|
||||||
{
|
{
|
||||||
var elementViewModel = _serviceProvider
|
var fileExtension = element.GetExtension<FileExtension>();
|
||||||
.GetInitableResolver<IElement, ITabViewModel, ItemViewModelType>(element, this, type)
|
|
||||||
.GetRequiredService<IElementViewModel>();
|
|
||||||
|
|
||||||
return elementViewModel;
|
if (fileExtension is not null)
|
||||||
|
{
|
||||||
|
var fileViewModel = _serviceProvider
|
||||||
|
.GetInitableResolver<IElement, FileExtension, ITabViewModel, ItemViewModelType>(
|
||||||
|
element, fileExtension, this, type)
|
||||||
|
.GetRequiredService<IFileViewModel>();
|
||||||
|
|
||||||
|
return fileViewModel;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var elementViewModel = _serviceProvider
|
||||||
|
.GetInitableResolver<IElement, ITabViewModel, ItemViewModelType>(element, this, type)
|
||||||
|
.GetRequiredService<IElementViewModel>();
|
||||||
|
|
||||||
|
return elementViewModel;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ArgumentException($"{nameof(item)} is not {nameof(IContainer)} neither {nameof(IElement)}");
|
throw new ArgumentException($"{nameof(item)} is not {nameof(IContainer)} neither {nameof(IElement)}");
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace FileTime.Core.Models;
|
||||||
|
|
||||||
|
public class ExtensionCollection : IEnumerable<object>
|
||||||
|
{
|
||||||
|
private readonly List<object> _extensions = new();
|
||||||
|
|
||||||
|
public ExtensionCollection()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExtensionCollection(IEnumerable<object> objects)
|
||||||
|
{
|
||||||
|
foreach (var obj in objects)
|
||||||
|
{
|
||||||
|
AddSafe(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddSafe(object obj)
|
||||||
|
{
|
||||||
|
var objType = obj.GetType();
|
||||||
|
if (_extensions.Any(i => i.GetType() == objType))
|
||||||
|
throw new ArgumentException($"Collection already contains an item with type {objType.FullName}");
|
||||||
|
|
||||||
|
_extensions.Add(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add<T>([DisallowNull] T obj, [CallerArgumentExpression("obj")] string? paramName = null)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(obj, paramName);
|
||||||
|
AddSafe(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove<T>()
|
||||||
|
{
|
||||||
|
_extensions.RemoveAll(i => i is T);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyExtensionCollection AsReadOnly() => new ReadOnlyExtensionCollection(this);
|
||||||
|
|
||||||
|
public IEnumerator<object> GetEnumerator() => _extensions.GetEnumerator();
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => _extensions.GetEnumerator();
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace FileTime.Core.Models;
|
|
||||||
|
|
||||||
public interface IFileElement : IElement
|
|
||||||
{
|
|
||||||
long Size { get; }
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Reactive.Linq;
|
||||||
using FileTime.Core.Enums;
|
using FileTime.Core.Enums;
|
||||||
using FileTime.Core.Services;
|
using FileTime.Core.Services;
|
||||||
|
|
||||||
@@ -19,4 +20,7 @@ public interface IItem
|
|||||||
string? Attributes { get; }
|
string? Attributes { get; }
|
||||||
AbsolutePathType Type { get; }
|
AbsolutePathType Type { get; }
|
||||||
IObservable<IEnumerable<Exception>> Exceptions { get; }
|
IObservable<IEnumerable<Exception>> Exceptions { get; }
|
||||||
|
ReadOnlyExtensionCollection Extensions { get; }
|
||||||
|
|
||||||
|
T? GetExtension<T>() => (T?)Extensions.FirstOrDefault(i => i is T);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace FileTime.Core.Models;
|
||||||
|
|
||||||
|
public class ReadOnlyExtensionCollection : IEnumerable<object>
|
||||||
|
{
|
||||||
|
private readonly ExtensionCollection _collection;
|
||||||
|
|
||||||
|
public ReadOnlyExtensionCollection(ExtensionCollection collection)
|
||||||
|
{
|
||||||
|
_collection = collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<object> GetEnumerator() => _collection.GetEnumerator();
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => _collection.GetEnumerator();
|
||||||
|
}
|
||||||
@@ -20,4 +20,6 @@ public interface IContentProvider : IContainer, IOnContainerEnter
|
|||||||
|
|
||||||
Task<List<IAbsolutePath>> GetItemsByContainerAsync(FullName fullName);
|
Task<List<IAbsolutePath>> GetItemsByContainerAsync(FullName fullName);
|
||||||
NativePath GetNativePath(FullName fullName);
|
NativePath GetNativePath(FullName fullName);
|
||||||
|
|
||||||
|
Task<byte[]?> GetContentAsync(IElement element, int? maxLength = null, CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Reactive.Subjects;
|
using System.Reactive.Subjects;
|
||||||
using DynamicData;
|
using DynamicData;
|
||||||
@@ -20,6 +21,7 @@ public record Container(
|
|||||||
string? Attributes,
|
string? Attributes,
|
||||||
IContentProvider Provider,
|
IContentProvider Provider,
|
||||||
IObservable<IEnumerable<Exception>> Exceptions,
|
IObservable<IEnumerable<Exception>> Exceptions,
|
||||||
|
ReadOnlyExtensionCollection Extensions,
|
||||||
IObservable<IObservable<IChangeSet<IAbsolutePath>>?> Items) : IContainer
|
IObservable<IObservable<IChangeSet<IAbsolutePath>>?> Items) : IContainer
|
||||||
{
|
{
|
||||||
BehaviorSubject<bool> IsLoading { get; } = new BehaviorSubject<bool>(false);
|
BehaviorSubject<bool> IsLoading { get; } = new BehaviorSubject<bool>(false);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
using FileTime.Core.Enums;
|
using FileTime.Core.Enums;
|
||||||
using FileTime.Core.Services;
|
using FileTime.Core.Services;
|
||||||
|
|
||||||
@@ -16,7 +17,8 @@ public record Element(
|
|||||||
bool CanRename,
|
bool CanRename,
|
||||||
string? Attributes,
|
string? Attributes,
|
||||||
IContentProvider Provider,
|
IContentProvider Provider,
|
||||||
IObservable<IEnumerable<Exception>> Exceptions) : IElement
|
IObservable<IEnumerable<Exception>> Exceptions,
|
||||||
|
ReadOnlyExtensionCollection Extensions) : IElement
|
||||||
{
|
{
|
||||||
public AbsolutePathType Type => AbsolutePathType.Element;
|
public AbsolutePathType Type => AbsolutePathType.Element;
|
||||||
}
|
}
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
using FileTime.Core.Enums;
|
|
||||||
using FileTime.Core.Services;
|
|
||||||
|
|
||||||
namespace FileTime.Core.Models;
|
|
||||||
|
|
||||||
public record FileElement(
|
|
||||||
string Name,
|
|
||||||
string DisplayName,
|
|
||||||
FullName FullName,
|
|
||||||
NativePath NativePath,
|
|
||||||
IAbsolutePath? Parent,
|
|
||||||
bool IsHidden,
|
|
||||||
bool IsExists,
|
|
||||||
DateTime? CreatedAt,
|
|
||||||
SupportsDelete CanDelete,
|
|
||||||
bool CanRename,
|
|
||||||
string? Attributes,
|
|
||||||
IContentProvider Provider,
|
|
||||||
IObservable<IEnumerable<Exception>> Exceptions,
|
|
||||||
long Size)
|
|
||||||
: Element(
|
|
||||||
Name,
|
|
||||||
DisplayName,
|
|
||||||
FullName,
|
|
||||||
NativePath,
|
|
||||||
Parent,
|
|
||||||
IsHidden,
|
|
||||||
IsExists,
|
|
||||||
CreatedAt,
|
|
||||||
CanDelete,
|
|
||||||
CanRename,
|
|
||||||
Attributes,
|
|
||||||
Provider,
|
|
||||||
Exceptions
|
|
||||||
), IFileElement;
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Reactive.Subjects;
|
using System.Reactive.Subjects;
|
||||||
using DynamicData;
|
using DynamicData;
|
||||||
@@ -8,7 +9,10 @@ namespace FileTime.Core.Services;
|
|||||||
|
|
||||||
public abstract class ContentProviderBase : IContentProvider
|
public abstract class ContentProviderBase : IContentProvider
|
||||||
{
|
{
|
||||||
|
private readonly ReadOnlyExtensionCollection _extensions;
|
||||||
|
|
||||||
protected BehaviorSubject<IObservable<IChangeSet<IAbsolutePath>>?> Items { get; } = new(null);
|
protected BehaviorSubject<IObservable<IChangeSet<IAbsolutePath>>?> Items { get; } = new(null);
|
||||||
|
protected ExtensionCollection Extensions { get; }
|
||||||
|
|
||||||
IObservable<IObservable<IChangeSet<IAbsolutePath>>?> IContainer.Items => Items;
|
IObservable<IObservable<IChangeSet<IAbsolutePath>>?> IContainer.Items => Items;
|
||||||
|
|
||||||
@@ -44,10 +48,14 @@ public abstract class ContentProviderBase : IContentProvider
|
|||||||
|
|
||||||
public IObservable<IEnumerable<Exception>> Exceptions => Observable.Return(Enumerable.Empty<Exception>());
|
public IObservable<IEnumerable<Exception>> Exceptions => Observable.Return(Enumerable.Empty<Exception>());
|
||||||
|
|
||||||
|
ReadOnlyExtensionCollection IItem.Extensions => _extensions;
|
||||||
|
|
||||||
protected ContentProviderBase(string name)
|
protected ContentProviderBase(string name)
|
||||||
{
|
{
|
||||||
DisplayName = Name = name;
|
DisplayName = Name = name;
|
||||||
FullName = new FullName(name);
|
FullName = new FullName(name);
|
||||||
|
Extensions = new ExtensionCollection();
|
||||||
|
_extensions = Extensions.AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual Task OnEnter() => Task.CompletedTask;
|
public virtual Task OnEnter() => Task.CompletedTask;
|
||||||
@@ -57,7 +65,8 @@ public abstract class ContentProviderBase : IContentProvider
|
|||||||
bool forceResolve = false,
|
bool forceResolve = false,
|
||||||
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
|
||||||
ItemInitializationSettings itemInitializationSettings = default)
|
ItemInitializationSettings itemInitializationSettings = default)
|
||||||
=> await GetItemByNativePathAsync(GetNativePath(fullName), forceResolve, forceResolvePathType, itemInitializationSettings);
|
=> await GetItemByNativePathAsync(GetNativePath(fullName), forceResolve, forceResolvePathType,
|
||||||
|
itemInitializationSettings);
|
||||||
|
|
||||||
public abstract Task<IItem> GetItemByNativePathAsync(
|
public abstract Task<IItem> GetItemByNativePathAsync(
|
||||||
NativePath nativePath,
|
NativePath nativePath,
|
||||||
@@ -67,4 +76,8 @@ public abstract class ContentProviderBase : IContentProvider
|
|||||||
|
|
||||||
public abstract Task<List<IAbsolutePath>> GetItemsByContainerAsync(FullName fullName);
|
public abstract Task<List<IAbsolutePath>> GetItemsByContainerAsync(FullName fullName);
|
||||||
public abstract NativePath GetNativePath(FullName fullName);
|
public abstract NativePath GetNativePath(FullName fullName);
|
||||||
|
|
||||||
|
public abstract Task<byte[]?> GetContentAsync(IElement element,
|
||||||
|
int? maxLength = null,
|
||||||
|
CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,7 @@ namespace FileTime.GuiApp.ViewModels;
|
|||||||
[Inject(typeof(IKeyInputHandlerService), PropertyName = "_keyInputHandlerService")]
|
[Inject(typeof(IKeyInputHandlerService), PropertyName = "_keyInputHandlerService")]
|
||||||
[Inject(typeof(IUserCommandHandlerService), PropertyAccessModifier = AccessModifier.Public)]
|
[Inject(typeof(IUserCommandHandlerService), PropertyAccessModifier = AccessModifier.Public)]
|
||||||
[Inject(typeof(LifecycleService), PropertyName = "_lifecycleService")]
|
[Inject(typeof(LifecycleService), PropertyName = "_lifecycleService")]
|
||||||
|
[Inject(typeof(IItemPreviewService), PropertyAccessModifier = AccessModifier.Public)]
|
||||||
public partial class MainWindowViewModel : IMainWindowViewModelBase
|
public partial class MainWindowViewModel : IMainWindowViewModelBase
|
||||||
{
|
{
|
||||||
public bool Loading => false;
|
public bool Loading => false;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:vm="using:FileTime.GuiApp.ViewModels"
|
xmlns:vm="using:FileTime.GuiApp.ViewModels"
|
||||||
xmlns:config="using:FileTime.GuiApp.Configuration"
|
xmlns:config="using:FileTime.GuiApp.Configuration"
|
||||||
|
xmlns:appCoreModels="using:FileTime.App.Core.Models"
|
||||||
Title="FileTime"
|
Title="FileTime"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
@@ -44,89 +45,97 @@
|
|||||||
<StackPanel
|
<StackPanel
|
||||||
Margin="20,10"
|
Margin="20,10"
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
<local:PathPresenter DataContext="{Binding AppState.SelectedTab^.CurrentLocation^.FullName.Path,Converter={StaticResource PathPreformatter}}"/>
|
<local:PathPresenter
|
||||||
|
DataContext="{Binding AppState.SelectedTab^.CurrentLocation^.FullName.Path,Converter={StaticResource PathPreformatter}}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Foreground="{StaticResource AccentBrush}"
|
Foreground="{StaticResource AccentBrush}"
|
||||||
Text="{Binding AppState.SelectedTab^.CurrentSelectedItem^.DisplayNameText}" />
|
Text="{Binding AppState.SelectedTab^.CurrentSelectedItem^.DisplayNameText}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid Grid.Row="1" RowDefinitions="Auto,Auto,Auto,Auto">
|
<Grid Grid.Row="1" RowDefinitions="Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
<Border CornerRadius="10" Background="{DynamicResource ContainerBackgroundBrush}" Padding="10" Margin="10">
|
<Border CornerRadius="10" Background="{DynamicResource ContainerBackgroundBrush}" Padding="10"
|
||||||
<Grid RowDefinitions="Auto,Auto">
|
Margin="10">
|
||||||
|
<Grid RowDefinitions="Auto,Auto">
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="0,0,0,10"
|
Margin="0,0,0,10"
|
||||||
Text="Drives" />
|
Text="Drives" />
|
||||||
|
|
||||||
<ItemsRepeater
|
<ItemsRepeater
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Items="{Binding AppState.RootDriveInfos.Collection}">
|
Items="{Binding AppState.RootDriveInfos.Collection}">
|
||||||
<ItemsRepeater.ItemTemplate>
|
<ItemsRepeater.ItemTemplate>
|
||||||
<DataTemplate x:DataType="vm:RootDriveInfo">
|
<DataTemplate x:DataType="vm:RootDriveInfo">
|
||||||
<Grid Classes="SidebarContainerPresenter" PointerPressed="OnHasContainerPointerPressed" Cursor="Hand">
|
<Grid Classes="SidebarContainerPresenter"
|
||||||
<Grid Margin="0,5" ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto,Auto">
|
PointerPressed="OnHasContainerPointerPressed" Cursor="Hand">
|
||||||
<Image
|
<Grid Margin="0,5" ColumnDefinitions="Auto,*,Auto"
|
||||||
Grid.RowSpan="2"
|
RowDefinitions="Auto,Auto">
|
||||||
Width="20"
|
<Image
|
||||||
Height="20"
|
Grid.RowSpan="2"
|
||||||
HorizontalAlignment="Left"
|
Width="20"
|
||||||
VerticalAlignment="Center"
|
Height="20"
|
||||||
Source="{SvgImage /Assets/material/folder.svg}" />
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Source="{SvgImage /Assets/material/folder.svg}" />
|
||||||
|
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="5,0,0,0"
|
Margin="5,0,0,0"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{Binding FullName}" />
|
Text="{Binding FullName}" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="5,0,0,0"
|
Margin="5,0,0,0"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Classes="ExtraSmallText"
|
Classes="ExtraSmallText"
|
||||||
Text="{Binding Label}" IsVisible="{Binding Label,Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
Text="{Binding Label}"
|
||||||
</StackPanel>
|
IsVisible="{Binding Label,Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel
|
<StackPanel
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Orientation="Horizontal"
|
Orientation="Horizontal"
|
||||||
VerticalAlignment="Center">
|
VerticalAlignment="Center">
|
||||||
|
|
||||||
<TextBlock Classes="SmallText" VerticalAlignment="Center" Text="{Binding Free, Converter={StaticResource FormatSizeConverter}, ConverterParameter=0}">
|
<TextBlock Classes="SmallText" VerticalAlignment="Center"
|
||||||
</TextBlock>
|
Text="{Binding Free, Converter={StaticResource FormatSizeConverter}, ConverterParameter=0}">
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
<TextBlock Classes="SmallText" VerticalAlignment="Center" Text=" / ">
|
<TextBlock Classes="SmallText" VerticalAlignment="Center"
|
||||||
</TextBlock>
|
Text=" / ">
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
<TextBlock Classes="SmallText" VerticalAlignment="Center" Text="{Binding Size, Converter={StaticResource FormatSizeConverter}, ConverterParameter=0}">
|
<TextBlock Classes="SmallText" VerticalAlignment="Center"
|
||||||
</TextBlock>
|
Text="{Binding Size, Converter={StaticResource FormatSizeConverter}, ConverterParameter=0}">
|
||||||
</StackPanel>
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
Margin="5,0,0,0"
|
Margin="5,0,0,0"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Grid.ColumnSpan="2"
|
Grid.ColumnSpan="2"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
MinWidth="100"
|
MinWidth="100"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
Maximum="100"
|
Maximum="100"
|
||||||
Value="{Binding UsedPercentage}" />
|
Value="{Binding UsedPercentage}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsRepeater.ItemTemplate>
|
</ItemsRepeater.ItemTemplate>
|
||||||
</ItemsRepeater>
|
</ItemsRepeater>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!--Border Grid.Row="1" CornerRadius="10" Background="{DynamicResource ContainerBackgroundBrush}" Padding="0,10" Margin="10">
|
<!--Border Grid.Row="1" CornerRadius="10" Background="{DynamicResource ContainerBackgroundBrush}" Padding="0,10" Margin="10">
|
||||||
<Grid RowDefinitions="Auto,Auto">
|
<Grid RowDefinitions="Auto,Auto">
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -222,7 +231,7 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Border-->
|
</Border-->
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
@@ -353,54 +362,68 @@
|
|||||||
|
|
||||||
<Grid Grid.Column="4">
|
<Grid Grid.Column="4">
|
||||||
<Grid
|
<Grid
|
||||||
IsVisible="{Binding AppState.SelectedTab^.SelectedsChildrenCollection.Collection, Converter={x:Static ObjectConverters.IsNotNull}, FallbackValue=False}">
|
IsVisible="{Binding ItemPreviewService.ItemPreview^,Converter={x:Static ObjectConverters.IsNull}}">
|
||||||
<ListBox
|
<Grid
|
||||||
x:Name="ChildItems"
|
IsVisible="{Binding AppState.SelectedTab^.SelectedsChildrenCollection.Collection, Converter={x:Static ObjectConverters.IsNotNull}, FallbackValue=False}">
|
||||||
x:CompileBindings="False"
|
<ListBox
|
||||||
AutoScrollToSelectedItem="True"
|
x:Name="ChildItems"
|
||||||
Classes="ContentListView"
|
x:CompileBindings="False"
|
||||||
IsVisible="{Binding AppState.SelectedTab^.SelectedsChildrenCollection.Collection.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}"
|
AutoScrollToSelectedItem="True"
|
||||||
Items="{Binding AppState.SelectedTab^.SelectedsChildrenCollection.Collection}">
|
Classes="ContentListView"
|
||||||
<ListBox.ItemTemplate>
|
IsVisible="{Binding AppState.SelectedTab^.SelectedsChildrenCollection.Collection.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}"
|
||||||
<DataTemplate x:DataType="corevm:IItemViewModel">
|
Items="{Binding AppState.SelectedTab^.SelectedsChildrenCollection.Collection}">
|
||||||
<local:ItemView />
|
<ListBox.ItemTemplate>
|
||||||
</DataTemplate>
|
<DataTemplate x:DataType="corevm:IItemViewModel">
|
||||||
</ListBox.ItemTemplate>
|
<local:ItemView />
|
||||||
</ListBox>
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
x:Name="ChildEmpty"
|
x:Name="ChildEmpty"
|
||||||
Margin="10"
|
Margin="10"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
x:CompileBindings="False"
|
x:CompileBindings="False"
|
||||||
FontWeight="Bold"
|
FontWeight="Bold"
|
||||||
Foreground="{DynamicResource ErrorBrush}"
|
Foreground="{DynamicResource ErrorBrush}"
|
||||||
IsVisible="{Binding AppState.SelectedTab^.SelectedsChildrenCollection.Collection.Count, Converter={StaticResource EqualityConverter}, ConverterParameter=0}">
|
IsVisible="{Binding AppState.SelectedTab^.SelectedsChildrenCollection.Collection.Count, Converter={StaticResource EqualityConverter}, ConverterParameter=0}">
|
||||||
Empty
|
Empty
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
IsVisible="{Binding AppState.SelectedTab^.SelectedsChildrenCollection, Converter={x:Static ObjectConverters.IsNull}, ConverterParameter=0, FallbackValue=False}"
|
||||||
|
RowDefinitions="Auto, Auto">
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,0,0,10"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Foreground="{DynamicResource ErrorBrush}"
|
||||||
|
Text="There were some errors while opening container."
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
|
<ItemsRepeater
|
||||||
|
Grid.Row="1"
|
||||||
|
Items="{Binding AppState.SelectedTab^.CurrentSelectedItem^.BaseItem.Exceptions^}">
|
||||||
|
<ItemsRepeater.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TextBlock
|
||||||
|
Margin="5,0,5,10"
|
||||||
|
Text="{Binding Converter={StaticResource ExceptionToStringConverter}}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsRepeater.ItemTemplate>
|
||||||
|
</ItemsRepeater>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
IsVisible="{Binding AppState.SelectedTab^.SelectedsChildrenCollection, Converter={x:Static ObjectConverters.IsNull}, ConverterParameter=0, FallbackValue=False}"
|
IsVisible="{Binding ItemPreviewService.ItemPreview^,Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||||
RowDefinitions="Auto, Auto">
|
<TextBlock HorizontalAlignment="Center" Text="Don't know how to preview this item." IsVisible="{Binding ItemPreviewService.ItemPreview^.Mode, Converter={StaticResource EqualityConverter}, ConverterParameter={x:Static appCoreModels:ItemPreviewMode.Unknown}}"/>
|
||||||
<TextBlock
|
<TextBlock HorizontalAlignment="Center" Text="Empty" IsVisible="{Binding ItemPreviewService.ItemPreview^.Mode, Converter={StaticResource EqualityConverter}, ConverterParameter={x:Static appCoreModels:ItemPreviewMode.Empty}}"/>
|
||||||
Margin="0,0,0,10"
|
<ScrollViewer IsVisible="{Binding ItemPreviewService.ItemPreview^.Mode, Converter={StaticResource EqualityConverter}, ConverterParameter={x:Static appCoreModels:ItemPreviewMode.Text}}">
|
||||||
HorizontalAlignment="Center"
|
<TextBox
|
||||||
Foreground="{DynamicResource ErrorBrush}"
|
IsReadOnly="True"
|
||||||
Text="There were some errors while opening container."
|
x:CompileBindings="False"
|
||||||
TextWrapping="Wrap" />
|
Text="{Binding ItemPreviewService.ItemPreview^.TextContent}" />
|
||||||
|
</ScrollViewer>
|
||||||
<ItemsRepeater
|
|
||||||
Grid.Row="1"
|
|
||||||
Items="{Binding AppState.SelectedTab^.CurrentSelectedItem^.BaseItem.Exceptions^}">
|
|
||||||
<ItemsRepeater.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock
|
|
||||||
Margin="5,0,5,10"
|
|
||||||
Text="{Binding Converter={StaticResource ExceptionToStringConverter}}" />
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsRepeater.ItemTemplate>
|
|
||||||
</ItemsRepeater>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Reactive.Linq;
|
|||||||
using System.Reactive.Subjects;
|
using System.Reactive.Subjects;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using DynamicData;
|
using DynamicData;
|
||||||
|
using FileTime.App.Core.Models;
|
||||||
using FileTime.Core.Enums;
|
using FileTime.Core.Enums;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using FileTime.Core.Services;
|
using FileTime.Core.Services;
|
||||||
@@ -133,6 +134,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
|||||||
"???",
|
"???",
|
||||||
this,
|
this,
|
||||||
nonNullExceptions,
|
nonNullExceptions,
|
||||||
|
new ExtensionCollection().AsReadOnly(),
|
||||||
Observable.Return<IObservable<IChangeSet<IAbsolutePath>>?>(null)
|
Observable.Return<IObservable<IChangeSet<IAbsolutePath>>?>(null)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -195,6 +197,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
|||||||
GetDirectoryAttributes(directoryInfo),
|
GetDirectoryAttributes(directoryInfo),
|
||||||
this,
|
this,
|
||||||
exceptions,
|
exceptions,
|
||||||
|
new ExtensionCollection().AsReadOnly(),
|
||||||
Observable.FromAsync(async () => await Task.Run(InitChildren))
|
Observable.FromAsync(async () => await Task.Run(InitChildren))
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -226,7 +229,12 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
|||||||
throw new Exception($"Path does not have parent: '{fileInfo.FullName}'");
|
throw new Exception($"Path does not have parent: '{fileInfo.FullName}'");
|
||||||
var parent = new AbsolutePath(this, parentFullName, AbsolutePathType.Container);
|
var parent = new AbsolutePath(this, parentFullName, AbsolutePathType.Container);
|
||||||
|
|
||||||
return new FileElement(
|
var extensions = new ExtensionCollection()
|
||||||
|
{
|
||||||
|
new FileExtension(fileInfo.Length)
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Element(
|
||||||
fileInfo.Name,
|
fileInfo.Name,
|
||||||
fileInfo.Name,
|
fileInfo.Name,
|
||||||
fullName,
|
fullName,
|
||||||
@@ -240,7 +248,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
|||||||
GetFileAttributes(fileInfo),
|
GetFileAttributes(fileInfo),
|
||||||
this,
|
this,
|
||||||
Observable.Return(Enumerable.Empty<Exception>()),
|
Observable.Return(Enumerable.Empty<Exception>()),
|
||||||
fileInfo.Length
|
extensions.AsReadOnly()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,4 +269,28 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
|||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && !path.StartsWith("/")) path = "/" + path;
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && !path.StartsWith("/")) path = "/" + path;
|
||||||
return new NativePath(path);
|
return new NativePath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async Task<byte[]?> GetContentAsync(IElement element, int? maxLength = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested) return null;
|
||||||
|
if (!File.Exists(element.NativePath!.Path))
|
||||||
|
throw new FileNotFoundException("File does not exist", element.NativePath.Path);
|
||||||
|
|
||||||
|
await using var reader = new FileStream(element.NativePath!.Path, FileMode.Open, FileAccess.Read, FileShare.Read,
|
||||||
|
bufferSize: 1, // bufferSize == 1 used to avoid unnecessary buffer in FileStream
|
||||||
|
FileOptions.Asynchronous | FileOptions.SequentialScan);
|
||||||
|
|
||||||
|
var realFileSize = new FileInfo(element.NativePath!.Path).Length;
|
||||||
|
|
||||||
|
var size = maxLength ?? realFileSize switch
|
||||||
|
{
|
||||||
|
> int.MaxValue => int.MaxValue,
|
||||||
|
_ => (int)realFileSize
|
||||||
|
};
|
||||||
|
var buffer = new byte[size];
|
||||||
|
await reader.ReadAsync(buffer, 0, size);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user