Fix RootDriveInfo not show every drive

This commit is contained in:
2023-09-06 09:29:29 +02:00
parent 0e6c0c5cd5
commit 67484ba56b
9 changed files with 82 additions and 70 deletions

View File

@@ -7,7 +7,6 @@ namespace FileTime.GuiApp.App.ViewModels;
public interface IGuiAppState : IAppState public interface IGuiAppState : IAppState
{ {
ObservableCollection<RootDriveInfo> RootDriveInfos { get; set; }
IReadOnlyList<PlaceInfo> Places { get; set; } IReadOnlyList<PlaceInfo> Places { get; set; }
ObservableCollection<string> PopupTexts { get; } ObservableCollection<string> PopupTexts { get; }
IObservable<GuiPanel> ActivePanel { get; } IObservable<GuiPanel> ActivePanel { get; }

View File

@@ -6,6 +6,7 @@ using FileTime.App.Core.ViewModels.Timeline;
using FileTime.App.FrequencyNavigation.Services; using FileTime.App.FrequencyNavigation.Services;
using FileTime.GuiApp.App.CloudDrives; using FileTime.GuiApp.App.CloudDrives;
using FileTime.GuiApp.App.Services; using FileTime.GuiApp.App.Services;
using FileTime.Providers.Local;
using FileTime.Providers.LocalAdmin; using FileTime.Providers.LocalAdmin;
namespace FileTime.GuiApp.App.ViewModels; namespace FileTime.GuiApp.App.ViewModels;
@@ -23,6 +24,7 @@ public interface IMainWindowViewModel : IMainWindowViewModelBase
ITimelineViewModel TimelineViewModel { get; } ITimelineViewModel TimelineViewModel { get; }
IPossibleCommandsViewModel PossibleCommands { get; } IPossibleCommandsViewModel PossibleCommands { get; }
ICloudDriveService CloudDriveService { get; } ICloudDriveService CloudDriveService { get; }
IRootDriveInfoService RootDriveInfoService { get; }
Action? ShowWindow { get; set; } Action? ShowWindow { get; set; }
Thickness IconStatusPanelMargin { get; } Thickness IconStatusPanelMargin { get; }
Task RunOrOpenItem(IItemViewModel itemViewModel); Task RunOrOpenItem(IItemViewModel itemViewModel);

View File

@@ -45,6 +45,7 @@ namespace FileTime.GuiApp.App.ViewModels;
[Inject(typeof(IPossibleCommandsViewModel), PropertyName = "PossibleCommands", PropertyAccessModifier = AccessModifier.Public)] [Inject(typeof(IPossibleCommandsViewModel), PropertyName = "PossibleCommands", PropertyAccessModifier = AccessModifier.Public)]
[Inject(typeof(IInstanceMessageHandler), PropertyName = "_instanceMessageHandler")] [Inject(typeof(IInstanceMessageHandler), PropertyName = "_instanceMessageHandler")]
[Inject(typeof(ICloudDriveService), PropertyAccessModifier = AccessModifier.Public)] [Inject(typeof(ICloudDriveService), PropertyAccessModifier = AccessModifier.Public)]
[Inject(typeof(IRootDriveInfoService), PropertyAccessModifier = AccessModifier.Public)]
public partial class MainWindowViewModel : IMainWindowViewModel public partial class MainWindowViewModel : IMainWindowViewModel
{ {
public bool Loading => false; public bool Loading => false;

View File

@@ -128,12 +128,13 @@
<ItemsRepeater <ItemsRepeater
Grid.Row="1" Grid.Row="1"
ItemsSource="{Binding AppState.RootDriveInfos}"> ItemsSource="{Binding RootDriveInfoService.RootDriveInfos}">
<ItemsRepeater.ItemTemplate> <ItemsRepeater.ItemTemplate>
<DataTemplate x:DataType="local1:RootDriveInfo"> <DataTemplate x:DataType="local1:RootDriveInfo">
<Grid <Grid
Classes="SidebarContainerPresenter" Classes="SidebarContainerPresenter"
PointerPressed="OnHasContainerPointerPressed"> PointerPressed="OnHasContainerPointerPressed"
ToolTip.Tip="{Binding FullName}">
<Grid <Grid
Margin="10,5" Margin="10,5"
ColumnDefinitions="Auto,*,Auto" ColumnDefinitions="Auto,*,Auto"
@@ -147,6 +148,7 @@
Source="{SvgImage /Assets/material/folder.svg}" /> Source="{SvgImage /Assets/material/folder.svg}" />
<StackPanel <StackPanel
Grid.Row="0"
Grid.Column="1" Grid.Column="1"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Center" VerticalAlignment="Center"
@@ -155,7 +157,7 @@
<TextBlock <TextBlock
Margin="5,0,0,0" Margin="5,0,0,0"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{Binding FullName}" /> Text="{Binding Name}" />
<TextBlock <TextBlock
Margin="5,0,0,0" Margin="5,0,0,0"

View File

@@ -13,17 +13,14 @@ public partial class GuiAppState : AppStateBase, IGuiAppState, IDisposable
{ {
private readonly BehaviorSubject<GuiPanel> _activePanel = new(GuiPanel.FileBrowser); private readonly BehaviorSubject<GuiPanel> _activePanel = new(GuiPanel.FileBrowser);
[Notify] private ObservableCollection<RootDriveInfo> _rootDriveInfos;
[Notify] private IReadOnlyList<PlaceInfo> _places = new List<PlaceInfo>(); [Notify] private IReadOnlyList<PlaceInfo> _places = new List<PlaceInfo>();
public ObservableCollection<string> PopupTexts { get; } = new(); public ObservableCollection<string> PopupTexts { get; } = new();
public IObservable<GuiPanel> ActivePanel { get; } public IObservable<GuiPanel> ActivePanel { get; }
public GuiAppState(IRootDriveInfoService rootDriveInfoService) public GuiAppState()
{ {
ActivePanel = _activePanel.AsObservable(); ActivePanel = _activePanel.AsObservable();
_rootDriveInfos = rootDriveInfoService.RootDriveInfos;
} }
public void SetActivePanel(GuiPanel newPanel) public void SetActivePanel(GuiPanel newPanel)

View File

@@ -5,5 +5,6 @@ namespace FileTime.Providers.Local;
public interface IRootDriveInfoService : IExitHandler public interface IRootDriveInfoService : IExitHandler
{ {
ObservableCollection<RootDriveInfo> RootDriveInfos { get; set; } ReadOnlyObservableCollection<RootDriveInfo> RootDriveInfos { get; set; }
ReadOnlyObservableCollection<DriveInfo> AllDrives { get; set; }
} }

View File

@@ -31,14 +31,7 @@ public partial class RootDriveInfo
_name = container.Name; _name = container.Name;
_fullName = _name; _fullName = driveInfo.Name;
try
{
_fullName = container.FullName?.Path[(container.Provider.FullName!.Path.Length + 1)..] ?? _fullName;
}
catch
{
}
Path = container.FullName ?? throw new NullReferenceException($"Container does not have a {nameof(FullName)}"); Path = container.FullName ?? throw new NullReferenceException($"Container does not have a {nameof(FullName)}");

View File

@@ -14,7 +14,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
private readonly ITimelessContentProvider _timelessContentProvider; private readonly ITimelessContentProvider _timelessContentProvider;
private readonly IContentProviderRegistry _contentProviderRegistry; private readonly IContentProviderRegistry _contentProviderRegistry;
private readonly bool _isCaseInsensitive; private readonly bool _isCaseInsensitive;
private readonly Lazy<ObservableCollection<RootDriveInfo>> _rootDriveInfos; private readonly Lazy<IRootDriveInfoService> _rootDriveInfo;
public LocalContentProvider( public LocalContentProvider(
ITimelessContentProvider timelessContentProvider, ITimelessContentProvider timelessContentProvider,
@@ -26,7 +26,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
_contentProviderRegistry = contentProviderRegistry; _contentProviderRegistry = contentProviderRegistry;
_isCaseInsensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); _isCaseInsensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
_rootDriveInfos = new Lazy<ObservableCollection<RootDriveInfo>>(() => serviceProvider.GetRequiredService<IRootDriveInfoService>().RootDriveInfos); _rootDriveInfo = new Lazy<IRootDriveInfoService>(serviceProvider.GetRequiredService<IRootDriveInfoService>);
SupportsContentStreams = true; SupportsContentStreams = true;
@@ -68,12 +68,26 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
public override ValueTask<VolumeSizeInfo?> GetVolumeSizeInfoAsync(FullName path) public override ValueTask<VolumeSizeInfo?> GetVolumeSizeInfoAsync(FullName path)
{ {
var rootDriveInfos = _rootDriveInfos.Value; var nativePath = GetNativePath(path);
var rootDriveInfo = rootDriveInfos.FirstOrDefault(d => path.Path.StartsWith(d.Path.Path));
var possibleRootDrives = _rootDriveInfo.Value.AllDrives.Where(d => nativePath.Path.StartsWith(d.RootDirectory.FullName)).ToArray();
var rootDrive = possibleRootDrives.Length == 0
? null
: possibleRootDrives.MaxBy(d => d.RootDirectory.FullName.Length);
return rootDrive is null
? ValueTask.FromResult<VolumeSizeInfo?>(null)
: ValueTask.FromResult<VolumeSizeInfo?>(new VolumeSizeInfo(rootDrive.TotalSize, rootDrive.AvailableFreeSpace));
/*var rootDriveInfos = _rootDriveInfo.Value;
var possibleRootDriveInfo = rootDriveInfos.RootDriveInfos.Where(d => path.Path.StartsWith(d.Path.Path)).ToArray();
var rootDriveInfo = possibleRootDriveInfo.Length == 0
? null
: possibleRootDriveInfo.MaxBy(d => d.FullName.Length);
if (rootDriveInfo is null) return ValueTask.FromResult<VolumeSizeInfo?>(null); if (rootDriveInfo is null) return ValueTask.FromResult<VolumeSizeInfo?>(null);
return ValueTask.FromResult<VolumeSizeInfo?>(new VolumeSizeInfo(rootDriveInfo.Size, rootDriveInfo.Free)); return ValueTask.FromResult<VolumeSizeInfo?>(new VolumeSizeInfo(rootDriveInfo.Size, rootDriveInfo.Free));*/
} }
public override async Task<IItem> GetItemByNativePathAsync(NativePath nativePath, public override async Task<IItem> GetItemByNativePathAsync(NativePath nativePath,
@@ -103,7 +117,8 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
return FileToElement(new FileInfo(path), pointInTime); return FileToElement(new FileInfo(path), pointInTime);
} }
var pathParts = path.Split(Path.DirectorySeparatorChar).SelectMany(p => p.Split(Constants.SeparatorChar)).ToArray(); var pathParts = path.Split(Path.DirectorySeparatorChar).SelectMany(p => p.Split(Constants.SeparatorChar))
.ToArray();
for (var i = pathParts.Length - 1; i > 0; i--) for (var i = pathParts.Length - 1; i > 0; i--)
{ {
@@ -116,7 +131,8 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
var subPath = string.Join(Constants.SeparatorChar, pathParts.Skip(i)); var subPath = string.Join(Constants.SeparatorChar, pathParts.Skip(i));
var resolvedItem = await subContentProvider.GetItemByFullNameAsync(element, new FullName(subPath), pointInTime, forceResolvePathType, itemInitializationSettings); var resolvedItem = await subContentProvider.GetItemByFullNameAsync(element, new FullName(subPath),
pointInTime, forceResolvePathType, itemInitializationSettings);
if (resolvedItem is not null) if (resolvedItem is not null)
{ {
@@ -155,7 +171,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
AbsolutePathType.Container => CreateEmptyContainer( AbsolutePathType.Container => CreateEmptyContainer(
nativePath, nativePath,
pointInTime, pointInTime,
new List<Exception> {innerException} new List<Exception> { innerException }
), ),
AbsolutePathType.Element => CreateEmptyElement(nativePath), AbsolutePathType.Element => CreateEmptyElement(nativePath),
_ => throw new Exception( _ => throw new Exception(
@@ -167,8 +183,9 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
public override ValueTask<NativePath?> GetSupportedPathPart(NativePath nativePath) public override ValueTask<NativePath?> GetSupportedPathPart(NativePath nativePath)
{ {
var path = nativePath.Path; var path = nativePath.Path;
var pathParts = path.Split(Path.DirectorySeparatorChar).SelectMany(p => p.Split(Constants.SeparatorChar)).ToArray(); var pathParts = path.Split(Path.DirectorySeparatorChar).SelectMany(p => p.Split(Constants.SeparatorChar))
.ToArray();
for (var i = pathParts.Length - 1; i > 0; i--) for (var i = pathParts.Length - 1; i > 0; i--)
{ {
var possiblePath = string.Join(Path.DirectorySeparatorChar, pathParts.Take(i)); var possiblePath = string.Join(Path.DirectorySeparatorChar, pathParts.Take(i));
@@ -440,7 +457,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
var finalSize = size switch var finalSize = size switch
{ {
> int.MaxValue => int.MaxValue, > int.MaxValue => int.MaxValue,
_ => (int) size _ => (int)size
}; };
var buffer = new byte[finalSize]; var buffer = new byte[finalSize];
var realSize = await reader.ReadAsync(buffer.AsMemory(0, finalSize), cancellationToken); var realSize = await reader.ReadAsync(buffer.AsMemory(0, finalSize), cancellationToken);

View File

@@ -1,6 +1,7 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Timeline;
using ObservableComputations; using ObservableComputations;
namespace FileTime.Providers.Local; namespace FileTime.Providers.Local;
@@ -8,66 +9,65 @@ namespace FileTime.Providers.Local;
public class RootDriveInfoService : IRootDriveInfoService public class RootDriveInfoService : IRootDriveInfoService
{ {
private readonly ILocalContentProvider _localContentProvider; private readonly ILocalContentProvider _localContentProvider;
private readonly List<DriveInfo> _rootDrives = new(); private readonly ObservableCollection<DriveInfo> _rootDrives = new();
private readonly ObservableCollection<DriveInfo> _allDrives = new();
private readonly OcConsumer _rootDriveInfosConsumer = new(); private readonly OcConsumer _rootDriveInfosConsumer = new();
public ObservableCollection<RootDriveInfo> RootDriveInfos { get; set; } public ReadOnlyObservableCollection<DriveInfo> AllDrives { get; set; }
public ReadOnlyObservableCollection<RootDriveInfo> RootDriveInfos { get; set; }
public RootDriveInfoService(ILocalContentProvider localContentProvider) public RootDriveInfoService(ILocalContentProvider localContentProvider)
{ {
_localContentProvider = localContentProvider; _localContentProvider = localContentProvider;
InitRootDrives(); var (rootDrives, allDrives) = GetRootDrives();
var rootDriveInfos = localContentProvider.Items.Selecting<AbsolutePath, (AbsolutePath Path, DriveInfo? Drive)>( foreach (var driveInfo in rootDrives)
i => MatchRootDrive(i)
)
.Filtering(t => IsNotNull(t.Drive))
.Selecting(t => Resolve(t))
.Filtering(t => t.Item is IContainer)
.Selecting(t => new RootDriveInfo(t.Drive, (IContainer) t.Item!))
.Ordering(d => d.Name);
rootDriveInfos.For(_rootDriveInfosConsumer);
RootDriveInfos = rootDriveInfos;
void InitRootDrives()
{ {
var drives = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) _rootDrives.Add(driveInfo);
? DriveInfo.GetDrives().Where(d => d.DriveType == DriveType.Fixed) }
: DriveInfo.GetDrives().Where(d =>
d.DriveType == DriveType.Fixed foreach (var driveInfo in allDrives)
{
_allDrives.Add(driveInfo);
}
var rootDriveInfos = _rootDrives.Selecting(r => GetContainer(r))
.Filtering(t => t.Item != null)
.Selecting(t => new RootDriveInfo(t.Drive, t.Item!))
.Ordering(d => d.Name)
.For(_rootDriveInfosConsumer);
RootDriveInfos = new ReadOnlyObservableCollection<RootDriveInfo>(rootDriveInfos);
AllDrives = new ReadOnlyObservableCollection<DriveInfo>(_allDrives);
(DriveInfo[] RootDrives, DriveInfo[] AllDrives) GetRootDrives()
{
var allDrives = DriveInfo.GetDrives();
var drives = DriveInfo.GetDrives().Where(d => d.DriveType is not DriveType.Unknown and not DriveType.Ram);
drives = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? drives
: drives.Where(d =>
d.TotalSize != 0
&& d.DriveFormat != "pstorefs" && d.DriveFormat != "pstorefs"
&& d.DriveFormat != "bpf_fs" && d.DriveFormat != "bpf_fs"
&& d.DriveFormat != "tracefs" && d.DriveFormat != "tracefs"
&& d.DriveFormat != "rpc_pipefs"
&& !d.RootDirectory.FullName.StartsWith("/snap/")); && !d.RootDirectory.FullName.StartsWith("/snap/"));
_rootDrives.Clear(); return (drives.ToArray(), allDrives);
_rootDrives.AddRange(drives);
} }
} }
private static bool IsNotNull(object? obj) => obj is not null; private (DriveInfo Drive, IContainer? Item) GetContainer(DriveInfo rootDriveInfo)
private static (IItem? Item, DriveInfo Drive) Resolve((AbsolutePath Path, DriveInfo? Drive) tuple)
{ {
var t = Task.Run(async () => await tuple.Path.ResolveAsyncSafe()); var task = Task.Run(
t.Wait(); async () => await _localContentProvider.GetItemByNativePathAsync(
return (Item: t.Result, Drive: tuple.Drive!); new NativePath(rootDriveInfo.RootDirectory.FullName),
} PointInTime.Present)
);
task.Wait();
private (AbsolutePath Path, DriveInfo? Drive) MatchRootDrive(AbsolutePath sourceItem) return (rootDriveInfo, task.Result as IContainer);
{
var rootDrive = _rootDrives.FirstOrDefault(d =>
{
var containerPath = _localContentProvider.GetNativePath(sourceItem.Path).Path;
var drivePath = d.Name.TrimEnd(Path.DirectorySeparatorChar);
return containerPath == drivePath
|| (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && containerPath == "/" &&
d.Name == "/");
});
return (Path: sourceItem, Drive: rootDrive);
} }
public Task ExitAsync(CancellationToken token = default) public Task ExitAsync(CancellationToken token = default)