Linux improvements

This commit is contained in:
2022-05-18 07:49:39 +02:00
parent becca2b62f
commit 255f3d34c9
3 changed files with 89 additions and 43 deletions

View File

@@ -1,6 +1,7 @@
using System.Reactive.Linq;
using System.Runtime.InteropServices;
using DynamicData;
using DynamicData.Binding;
using FileTime.App.Core.Models;
using FileTime.App.Core.Services;
using FileTime.Core.Models;
@@ -13,40 +14,48 @@ namespace FileTime.GuiApp.Services;
public class RootDriveInfoService : IStartupHandler
{
private readonly SourceList<DriveInfo> _rootDrives = new();
private readonly IObservable<IChangeSet<IAbsolutePath>> _localContentProviderStream;
public RootDriveInfoService(IGuiAppState guiAppState, ILocalContentProvider localContentProvider)
{
InitRootDrives();
var localContentProviderAsList = new SourceList<IAbsolutePath>();
localContentProviderAsList.Add(new AbsolutePath(localContentProvider));
_localContentProviderStream = localContentProviderAsList.Connect();
var rootDriveInfos = Observable.CombineLatest(
localContentProvider.Items,
_rootDrives.Connect().StartWithEmpty().ToCollection(),
(items, drives) =>
{
return items is null
? Observable.Empty<IChangeSet<(IAbsolutePath Path, DriveInfo? Drive)>>()
: items
.Transform(i => (Path: i, Drive: drives.FirstOrDefault(d =>
{
var asd1 = localContentProvider.GetNativePath(i.Path).Path;
var asd2 = d.Name.TrimEnd(Path.DirectorySeparatorChar);
return asd1 == asd2;
})))
.Filter(t => t.Drive is not null);
}
)
.Switch()
.TransformAsync(async t => (Item: await t.Path.ResolveAsyncSafe(), Drive: t.Drive!))
.Filter(t => t.Item is IContainer)
.Transform(t => (Container: (IContainer)t.Item!, t.Drive))
.Transform(t => new RootDriveInfo(t.Drive, t.Container));
localContentProvider.Items,
_rootDrives.Connect().StartWithEmpty().ToCollection(),
(items, drives) =>
{
return items is null
? Observable.Empty<IChangeSet<(IAbsolutePath Path, DriveInfo? Drive)>>()
: items!
.Or(new[] { _localContentProviderStream })
.Transform(i => (Path: i, Drive: drives.FirstOrDefault(d =>
{
var containerPath = localContentProvider.GetNativePath(i.Path).Path;
var drivePath = d.Name.TrimEnd(Path.DirectorySeparatorChar);
return containerPath == drivePath
|| (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && containerPath == "/" && d.Name == "/");
})))
.Filter(t => t.Drive is not null);
}
)
.Switch()
.TransformAsync(async t => (Item: await t.Path.ResolveAsyncSafe(), Drive: t.Drive!))
.Filter(t => t.Item is IContainer)
.Transform(t => (Container: (IContainer)t.Item!, t.Drive))
.Transform(t => new RootDriveInfo(t.Drive, t.Container))
.Sort(SortExpressionComparer<RootDriveInfo>.Ascending(d => d.Name));
guiAppState.RootDriveInfos = new BindedCollection<RootDriveInfo>(rootDriveInfos);
void InitRootDrives()
{
var driveInfos = new List<RootDriveInfo>();
IEnumerable<DriveInfo> drives = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
var drives = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? DriveInfo.GetDrives().Where(d => d.DriveType == DriveType.Fixed)
: DriveInfo.GetDrives().Where(d =>
d.DriveType == DriveType.Fixed

View File

@@ -20,9 +20,6 @@
Opened="OnWindowOpened"
TransparencyLevelHint="Blur"
mc:Ignorable="d">
<Design.DataContext>
<vm:MainWindowViewModel />
</Design.DataContext>
<Grid Background="{DynamicResource AppBackgroundBrush}">
<Grid

View File

@@ -54,15 +54,18 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
{
if ((path?.Length ?? 0) == 0)
{
return Task.FromResult((IItem) this);
return Task.FromResult((IItem)this);
}
else if (Directory.Exists(path))
{
return Task.FromResult((IItem) DirectoryToContainer(new DirectoryInfo(path!.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar), !itemInitializationSettings.SkipChildInitialization));
return Task.FromResult((IItem)DirectoryToContainer(
new DirectoryInfo(path!.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar),
!itemInitializationSettings.SkipChildInitialization)
);
}
else if (File.Exists(path))
{
return Task.FromResult((IItem) FileToElement(new FileInfo(path)));
return Task.FromResult((IItem)FileToElement(new FileInfo(path)));
}
var type = forceResolvePathType switch
@@ -80,19 +83,31 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
}
catch (Exception e)
{
if (!forceResolve) throw new Exception($"Could not resolve path '{nativePath.Path}' and {nameof(forceResolve)} is false.", e);
if (!forceResolve)
{
throw new Exception(
$"Could not resolve path '{nativePath.Path}' and {nameof(forceResolve)} is false.",
e
);
}
innerException = e;
}
return forceResolvePathType switch
{
AbsolutePathType.Container => Task.FromResult((IItem) CreateEmptyContainer(nativePath, Observable.Return(new List<Exception>() {innerException}))),
AbsolutePathType.Container => Task.FromResult(
(IItem)CreateEmptyContainer(nativePath, Observable.Return(new List<Exception>() { innerException }))
),
AbsolutePathType.Element => Task.FromResult(CreateEmptyElement(nativePath)),
_ => throw new Exception($"Could not resolve path '{nativePath.Path}' and could not force create, because {nameof(forceResolvePathType)} is {nameof(AbsolutePathType.Unknown)}.", innerException)
_ => throw new Exception(
$"Could not resolve path '{nativePath.Path}' and could not force create, because {nameof(forceResolvePathType)} is {nameof(AbsolutePathType.Unknown)}.",
innerException)
};
}
private Container CreateEmptyContainer(NativePath nativePath, IObservable<IEnumerable<Exception>>? exceptions = null)
private Container CreateEmptyContainer(NativePath nativePath,
IObservable<IEnumerable<Exception>>? exceptions = null)
{
var nonNullExceptions = exceptions ?? Observable.Return(Enumerable.Empty<Exception>());
var name = nativePath.Path.Split(Path.DirectorySeparatorChar).LastOrDefault() ?? "???";
@@ -127,9 +142,22 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
throw new NotImplementedException();
}
public override Task<List<IAbsolutePath>> GetItemsByContainerAsync(FullName fullName) => Task.FromResult(GetItemsByContainer(fullName));
private List<IAbsolutePath> GetItemsByContainer(FullName fullName) => GetItemsByContainer(new DirectoryInfo(GetNativePath(fullName).Path));
private List<IAbsolutePath> GetItemsByContainer(DirectoryInfo directoryInfo) => directoryInfo.GetDirectories().Select(DirectoryToAbsolutePath).Concat(directoryInfo.GetFiles().Select(FileToAbsolutePath)).ToList();
public override Task<List<IAbsolutePath>> GetItemsByContainerAsync(FullName fullName)
=> Task.FromResult(GetItemsByContainer(fullName));
private List<IAbsolutePath> GetItemsByContainer(FullName fullName)
=> GetItemsByContainer(new DirectoryInfo(GetNativePath(fullName).Path));
private List<IAbsolutePath> GetItemsByContainer(DirectoryInfo directoryInfo)
=> directoryInfo
.GetDirectories()
.Select(DirectoryToAbsolutePath)
.Concat(
directoryInfo
.GetFiles()
.Select(FileToAbsolutePath)
)
.ToList();
private IAbsolutePath DirectoryToAbsolutePath(DirectoryInfo directoryInfo)
{
@@ -153,11 +181,11 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
AbsolutePathType.Container);
var exceptions = new BehaviorSubject<IEnumerable<Exception>>(Enumerable.Empty<Exception>());
return new(
return new Container(
directoryInfo.Name,
directoryInfo.Name,
fullName,
new(directoryInfo.FullName),
new NativePath(directoryInfo.FullName),
parent,
(directoryInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden,
directoryInfo.Exists,
@@ -175,7 +203,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
SourceList<IAbsolutePath>? result = null;
try
{
var items = initializeChildren ? (List<IAbsolutePath>?) GetItemsByContainer(directoryInfo) : null;
var items = initializeChildren ? (List<IAbsolutePath>?)GetItemsByContainer(directoryInfo) : null;
if (items != null)
{
result = new SourceList<IAbsolutePath>();
@@ -184,7 +212,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
}
catch (Exception e)
{
exceptions.OnNext(new List<Exception>() {e});
exceptions.OnNext(new List<Exception>() { e });
}
return Task.FromResult(result?.Connect());
@@ -194,14 +222,15 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
private Element FileToElement(FileInfo fileInfo)
{
var fullName = GetFullName(fileInfo);
var parentFullName = fullName.GetParent() ?? throw new Exception($"Path does not have parent: '{fileInfo.FullName}'");
var parentFullName = fullName.GetParent() ??
throw new Exception($"Path does not have parent: '{fileInfo.FullName}'");
var parent = new AbsolutePath(this, parentFullName, AbsolutePathType.Container);
return new FileElement(
fileInfo.Name,
fileInfo.Name,
fullName,
new(fileInfo.FullName),
new NativePath(fileInfo.FullName),
parent,
(fileInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden,
fileInfo.Exists,
@@ -216,9 +245,20 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
}
private FullName GetFullName(DirectoryInfo directoryInfo) => GetFullName(directoryInfo.FullName);
private FullName GetFullName(FileInfo fileInfo) => GetFullName(fileInfo.FullName);
private FullName GetFullName(NativePath nativePath) => GetFullName(nativePath.Path);
private FullName GetFullName(string nativePath) => new((Name + Constants.SeparatorChar + string.Join(Constants.SeparatorChar, nativePath.Split(Path.DirectorySeparatorChar))).TrimEnd(Constants.SeparatorChar));
public override NativePath GetNativePath(FullName fullName) => new(string.Join(Path.DirectorySeparatorChar, fullName.Path.Split(Constants.SeparatorChar).Skip(1)));
private FullName GetFullName(string nativePath) =>
new((Name + Constants.SeparatorChar +
string.Join(Constants.SeparatorChar,
nativePath.TrimStart(Constants.SeparatorChar).Split(Path.DirectorySeparatorChar)))
.TrimEnd(Constants.SeparatorChar));
public override NativePath GetNativePath(FullName fullName)
{
var path = string.Join(Path.DirectorySeparatorChar, fullName.Path.Split(Constants.SeparatorChar).Skip(1));
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && !path.StartsWith("/")) path = "/" + path;
return new NativePath(path);
}
}