Linux improvements
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user