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

View File

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

View File

@@ -58,7 +58,10 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
} }
else if (Directory.Exists(path)) 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)) else if (File.Exists(path))
{ {
@@ -80,19 +83,31 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
} }
catch (Exception e) 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; innerException = e;
} }
return forceResolvePathType switch 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)), 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 nonNullExceptions = exceptions ?? Observable.Return(Enumerable.Empty<Exception>());
var name = nativePath.Path.Split(Path.DirectorySeparatorChar).LastOrDefault() ?? "???"; var name = nativePath.Path.Split(Path.DirectorySeparatorChar).LastOrDefault() ?? "???";
@@ -127,9 +142,22 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override Task<List<IAbsolutePath>> GetItemsByContainerAsync(FullName fullName) => Task.FromResult(GetItemsByContainer(fullName)); public override Task<List<IAbsolutePath>> GetItemsByContainerAsync(FullName fullName)
private List<IAbsolutePath> GetItemsByContainer(FullName fullName) => GetItemsByContainer(new DirectoryInfo(GetNativePath(fullName).Path)); => Task.FromResult(GetItemsByContainer(fullName));
private List<IAbsolutePath> GetItemsByContainer(DirectoryInfo directoryInfo) => directoryInfo.GetDirectories().Select(DirectoryToAbsolutePath).Concat(directoryInfo.GetFiles().Select(FileToAbsolutePath)).ToList();
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) private IAbsolutePath DirectoryToAbsolutePath(DirectoryInfo directoryInfo)
{ {
@@ -153,11 +181,11 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
AbsolutePathType.Container); AbsolutePathType.Container);
var exceptions = new BehaviorSubject<IEnumerable<Exception>>(Enumerable.Empty<Exception>()); var exceptions = new BehaviorSubject<IEnumerable<Exception>>(Enumerable.Empty<Exception>());
return new( return new Container(
directoryInfo.Name, directoryInfo.Name,
directoryInfo.Name, directoryInfo.Name,
fullName, fullName,
new(directoryInfo.FullName), new NativePath(directoryInfo.FullName),
parent, parent,
(directoryInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden, (directoryInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden,
directoryInfo.Exists, directoryInfo.Exists,
@@ -194,14 +222,15 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
private Element FileToElement(FileInfo fileInfo) private Element FileToElement(FileInfo fileInfo)
{ {
var fullName = GetFullName(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); var parent = new AbsolutePath(this, parentFullName, AbsolutePathType.Container);
return new FileElement( return new FileElement(
fileInfo.Name, fileInfo.Name,
fileInfo.Name, fileInfo.Name,
fullName, fullName,
new(fileInfo.FullName), new NativePath(fileInfo.FullName),
parent, parent,
(fileInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden, (fileInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden,
fileInfo.Exists, fileInfo.Exists,
@@ -216,9 +245,20 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
} }
private FullName GetFullName(DirectoryInfo directoryInfo) => GetFullName(directoryInfo.FullName); private FullName GetFullName(DirectoryInfo directoryInfo) => GetFullName(directoryInfo.FullName);
private FullName GetFullName(FileInfo fileInfo) => GetFullName(fileInfo.FullName); private FullName GetFullName(FileInfo fileInfo) => GetFullName(fileInfo.FullName);
private FullName GetFullName(NativePath nativePath) => GetFullName(nativePath.Path); 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);
}
} }