Linux improvements
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -20,9 +20,6 @@
|
||||
Opened="OnWindowOpened"
|
||||
TransparencyLevelHint="Blur"
|
||||
mc:Ignorable="d">
|
||||
<Design.DataContext>
|
||||
<vm:MainWindowViewModel />
|
||||
</Design.DataContext>
|
||||
|
||||
<Grid Background="{DynamicResource AppBackgroundBrush}">
|
||||
<Grid
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user