New reactive core WIP

This commit is contained in:
2023-07-21 22:15:48 +02:00
parent 342fc0d047
commit b61c204e49
61 changed files with 1605 additions and 413 deletions

View File

@@ -81,7 +81,7 @@ public class TabPersistenceService : ITabPersistenceService
{
if (!File.Exists(_settingsPath))
{
CreateEmptyTab();
await CreateEmptyTab();
return;
}
@@ -99,12 +99,12 @@ public class TabPersistenceService : ITabPersistenceService
_logger.LogError(e, "Unknown exception while restoring app state");
}
CreateEmptyTab();
await CreateEmptyTab();
void CreateEmptyTab()
async Task CreateEmptyTab()
{
var tab = _serviceProvider.GetInitableResolver<IContainer>(_localContentProvider)
.GetRequiredService<ITab>();
var tab = await _serviceProvider.GetAsyncInitableResolver<IContainer>(_localContentProvider)
.GetRequiredServiceAsync<ITab>();
var tabViewModel = _serviceProvider.GetInitableResolver(tab, 1).GetRequiredService<ITabViewModel>();
_appState.AddTab(tabViewModel);
@@ -159,8 +159,8 @@ public class TabPersistenceService : ITabPersistenceService
if (container == null) continue;
var tabToLoad = _serviceProvider.GetInitableResolver(container)
.GetRequiredService<ITab>();
var tabToLoad = await _serviceProvider.GetAsyncInitableResolver(container)
.GetRequiredServiceAsync<ITab>();
var tabViewModel = _serviceProvider.GetInitableResolver(tabToLoad, tab.Number)
.GetRequiredService<ITabViewModel>();
@@ -205,7 +205,7 @@ public class TabPersistenceService : ITabPersistenceService
var tabStates = new List<TabState>();
foreach (var tab in _appState.Tabs)
{
var currentLocation = tab.CachedCurrentLocation;
var currentLocation = tab.CurrentLocation.Value;
if (currentLocation is null) continue;
tabStates.Add(new TabState(currentLocation.FullName!, tab.TabNumber));
}

View File

@@ -0,0 +1,115 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace FileTime.App.Core.Services;
public sealed class RefreshSmoothnessCalculator : IRefreshSmoothnessCalculator, INotifyPropertyChanged
{
private const int MaxSampleTimeInSeconds = 10;
private const int MaxDelayBetweenRefreshes = 400;
private readonly TimeSpan _maxDelay = TimeSpan.FromSeconds(MaxSampleTimeInSeconds);
private readonly TimeSpan _defaultRefreshDelay = TimeSpan.FromMilliseconds(200);
private readonly Queue<DateTime> _changeTimes = new();
private readonly object _lock = new();
private TimeSpan _refreshDelay;
public TimeSpan RefreshDelay
{
get => _refreshDelay;
private set
{
if (value.Equals(_refreshDelay)) return;
_refreshDelay = value;
OnPropertyChanged();
}
}
public RefreshSmoothnessCalculator()
{
_refreshDelay = _defaultRefreshDelay;
}
public void RegisterChange() => RegisterChange(DateTime.Now);
public void RegisterChange(DateTime changeTime)
{
lock (_lock)
{
if (_changeTimes.Count > 0 && Math.Abs((_changeTimes.Last() - changeTime).TotalMilliseconds) < 5) return;
CleanList(DateTime.Now);
_changeTimes.Enqueue(changeTime);
}
}
private void CleanList(DateTime now)
{
while (_changeTimes.Count > 0)
{
var item = _changeTimes.Peek();
if (now - item < _maxDelay) return;
_changeTimes.Dequeue();
}
}
public void RecalculateSmoothness()
{
lock (_lock)
{
if (_changeTimes.Count < 2)
{
RefreshDelay = _defaultRefreshDelay;
return;
}
var now = DateTime.Now;
CleanList(now);
var queue = new Queue<DateTime>(_changeTimes);
var values = new List<(double score, double weight)>(queue.Count - 1);
var previousChangeTime = queue.Dequeue();
var biggestDelay = now - previousChangeTime;
while (queue.Count > 0)
{
var changeTime = queue.Dequeue();
var (score, weight) = CalculateScoreAndWeight(changeTime, previousChangeTime, biggestDelay);
values.Add((score, weight));
previousChangeTime = changeTime;
}
var combinedScore = values.Sum(i => i.weight * i.score) / values.Sum(i => i.weight);
var normalizedCombinedScore = (combinedScore * 1.2 - 0.1);
if (normalizedCombinedScore < 0) normalizedCombinedScore = 0;
else if (normalizedCombinedScore > 1) normalizedCombinedScore = 1;
var finalDelay = normalizedCombinedScore * MaxDelayBetweenRefreshes;
RefreshDelay = TimeSpan.FromMilliseconds(finalDelay);
(double score, double weight) CalculateScoreAndWeight(DateTime changeTime, DateTime previousChangeTime, TimeSpan biggestDelay)
{
var delayToPrevious = changeTime - previousChangeTime;
var delayToNow = now - changeTime;
var toNowRatio = (delayToNow.TotalMilliseconds / biggestDelay.TotalMilliseconds);
var score = 1 - (delayToPrevious.TotalMilliseconds / biggestDelay.TotalMilliseconds);
var weight = 1 - toNowRatio;
if (score < 0) score = 0;
else if (score > 1) score = 1;
return (score, weight);
}
}
}
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

View File

@@ -1,6 +1,7 @@
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Text.RegularExpressions;
using DeclarativeProperty;
using DynamicData;
using FileTime.App.Core.Interactions;
using FileTime.App.Core.Models;
@@ -25,7 +26,7 @@ namespace FileTime.App.Core.Services.UserCommandHandler;
public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServiceBase
{
private ITabViewModel? _selectedTab;
private IItemViewModel? _currentSelectedItem;
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
private readonly IUserCommandHandlerService _userCommandHandlerService;
private readonly IClipboardService _clipboardService;
private readonly IUserCommunicationService _userCommunicationService;
@@ -35,7 +36,7 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
private readonly IServiceProvider _serviceProvider;
private readonly ISystemClipboardService _systemClipboardService;
private readonly BindedCollection<FullName>? _markedItems;
private IContainer? _currentLocation;
private IDeclarativeProperty<IContainer?>? _currentLocation;
public ItemManipulationUserCommandHandlerService(
IAppState appState,
@@ -84,7 +85,7 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
{
list.AddRange(_markedItems!.Collection!);
}
else if(_currentSelectedItem?.BaseItem?.FullName is { } selectedItemName)
else if(_currentSelectedItem?.Value?.BaseItem?.FullName is { } selectedItemName)
{
list.Add(selectedItemName);
}
@@ -106,20 +107,20 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
private async Task PasteFilesFromClipboardAsync(TransportMode mode)
{
if (_currentLocation?.FullName is not { }) return;
if (_currentLocation?.Value?.FullName is not { }) return;
var files = (await _systemClipboardService.GetFilesAsync()).ToList();
var copyCommandFactory = _serviceProvider.GetRequiredService<FileTime.Core.Command.Copy.CopyCommandFactory>();
var copyCommand = copyCommandFactory.GenerateCommand(files, mode, _currentLocation.FullName);
var copyCommand = copyCommandFactory.GenerateCommand(files, mode, _currentLocation.Value.FullName);
await AddCommandAsync(copyCommand);
}
private async Task MarkItemAsync()
{
if (_selectedTab == null || _currentSelectedItem?.BaseItem?.FullName == null) return;
if (_selectedTab == null || _currentSelectedItem?.Value?.BaseItem?.FullName == null) return;
_selectedTab.ToggleMarkedItem(_currentSelectedItem.BaseItem.FullName);
_selectedTab.ToggleMarkedItem(_currentSelectedItem.Value.BaseItem.FullName);
await _userCommandHandlerService.HandleCommandAsync(MoveCursorDownCommand.Instance);
}
@@ -137,9 +138,9 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
_selectedTab?.ClearMarkedItems();
}
else if (_currentSelectedItem?.BaseItem != null)
else if (_currentSelectedItem?.Value?.BaseItem != null)
{
var item = _currentSelectedItem.BaseItem;
var item = _currentSelectedItem.Value.BaseItem;
_clipboardService.AddContent(
item.FullName
?? throw new ArgumentException($"{nameof(item.FullName)} can not be null.", nameof(item))
@@ -168,7 +169,7 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
//TODO: check _currentLocation?.FullName
var commandFactory = (ITransportationCommandFactory) _serviceProvider.GetRequiredService(_clipboardService.CommandFactoryType);
var command = commandFactory.GenerateCommand(_clipboardService.Content, mode, _currentLocation?.FullName);
var command = commandFactory.GenerateCommand(_clipboardService.Content, mode, _currentLocation?.Value?.FullName);
_clipboardService.Clear();
@@ -186,10 +187,10 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
//TODO: message on empty result
var newContainerName = containerNameInput.Value;
if (_currentLocation?.FullName is null || newContainerName is null) return;
if (_currentLocation?.Value?.FullName is null || newContainerName is null) return;
var command = _serviceProvider
.GetInitableResolver(_currentLocation.FullName, newContainerName)
.GetInitableResolver(_currentLocation.Value.FullName, newContainerName)
.GetRequiredService<CreateContainerCommand>();
await AddCommandAsync(command);
}
@@ -203,10 +204,10 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
//TODO: message on empty result
var newContainerName = containerNameInput.Value;
if (_currentLocation?.FullName is null || newContainerName is null) return;
if (_currentLocation?.Value?.FullName is null || newContainerName is null) return;
var command = _serviceProvider
.GetInitableResolver(_currentLocation.FullName, newContainerName)
.GetInitableResolver(_currentLocation.Value.FullName, newContainerName)
.GetRequiredService<FileTime.Core.Command.CreateElement.CreateElementCommand>();
await AddCommandAsync(command);
}
@@ -342,9 +343,9 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
}
else
{
if (_currentSelectedItem?.BaseItem?.FullName is null) return;
if (_currentSelectedItem?.Value?.BaseItem?.FullName is null) return;
var item = await _timelessContentProvider.GetItemByFullNameAsync(_currentSelectedItem.BaseItem.FullName, PointInTime.Present);
var item = await _timelessContentProvider.GetItemByFullNameAsync(_currentSelectedItem.Value.BaseItem.FullName, PointInTime.Present);
if (item is null) return;
@@ -410,11 +411,11 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
{
itemsToDelete = new List<FullName>(_markedItems!.Collection!);
}
else if (_currentSelectedItem?.BaseItem?.FullName is not null)
else if (_currentSelectedItem?.Value?.BaseItem?.FullName is not null)
{
itemsToDelete = new List<FullName>()
{
_currentSelectedItem.BaseItem.FullName
_currentSelectedItem.Value.BaseItem.FullName
};
}
@@ -425,7 +426,7 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
var resolvedOnlyItem = await _timelessContentProvider.GetItemByFullNameAsync(itemsToDelete[0], PointInTime.Present);
if (resolvedOnlyItem is IContainer {AllowRecursiveDeletion: true} onlyContainer
&& onlyContainer.ItemsCollection.Any())
&& onlyContainer.Items.Count > 0)
{
questionText = $"The container '{onlyContainer.DisplayName}' is not empty. Proceed with delete?";
}
@@ -461,8 +462,6 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
_selectedTab?.ClearMarkedItems();
}
private async Task AddCommandAsync(ICommand command)
{
await _commandScheduler.AddCommand(command);
}
private async Task AddCommandAsync(ICommand command)
=> await _commandScheduler.AddCommand(command);
}

View File

@@ -1,3 +1,5 @@
using System.Collections.ObjectModel;
using DeclarativeProperty;
using FileTime.App.CommandPalette.Services;
using FileTime.App.Core.Extensions;
using FileTime.App.Core.Models.Enums;
@@ -25,9 +27,9 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
private readonly IFrequencyNavigationService _frequencyNavigationService;
private readonly ICommandPaletteService _commandPaletteService;
private ITabViewModel? _selectedTab;
private IContainer? _currentLocation;
private IItemViewModel? _currentSelectedItem;
private IEnumerable<IItemViewModel> _currentItems = Enumerable.Empty<IItemViewModel>();
private IDeclarativeProperty<IContainer?>? _currentLocation;
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
private IDeclarativeProperty<ObservableCollection<IItemViewModel>?>? _currentItems;
private ViewMode _viewMode;
public NavigationUserCommandHandlerService(
@@ -121,7 +123,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
private async Task GoToRoot()
{
var root = _currentLocation;
var root = _currentLocation?.Value;
if (root is null) return;
while (true)
@@ -138,21 +140,25 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
private async Task GoToProvider()
{
if (_currentLocation is null) return;
if (_currentLocation?.Value is null) return;
await _userCommandHandlerService.HandleCommandAsync(
new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, _currentLocation.Provider)));
new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, _currentLocation.Value.Provider)));
}
private async Task Refresh()
{
if (_currentLocation?.FullName is null) return;
if (_currentLocation?.Value?.FullName is null) return;
var refreshedItem =
await _timelessContentProvider.GetItemByFullNameAsync(_currentLocation.FullName, PointInTime.Present);
await _timelessContentProvider.GetItemByFullNameAsync(_currentLocation.Value.FullName, PointInTime.Present);
if (refreshedItem is not IContainer refreshedContainer) return;
_selectedTab?.Tab?.ForceSetCurrentLocation(refreshedContainer);
if (_selectedTab?.Tab is { } tab)
{
await tab.ForceSetCurrentLocation(refreshedContainer);
}
}
private async Task OpenContainer(OpenContainerCommand command)
@@ -160,91 +166,100 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
var resolvedPath = await command.Path.ResolveAsync();
if (resolvedPath is not IContainer resolvedContainer) return;
_selectedTab?.Tab?.SetCurrentLocation(resolvedContainer);
if (_selectedTab?.Tab is { } tab)
{
await tab.SetCurrentLocation(resolvedContainer);
}
}
private Task OpenSelected()
private async Task OpenSelected()
{
if (_currentSelectedItem is not IContainerViewModel containerViewModel || containerViewModel.Container is null)
return Task.CompletedTask;
if (_currentSelectedItem?.Value is not IContainerViewModel containerViewModel || containerViewModel.Container is null)
return;
_appState.RapidTravelText = "";
_selectedTab?.Tab?.SetCurrentLocation(containerViewModel.Container);
return Task.CompletedTask;
if (_selectedTab?.Tab is { } tab)
{
await tab.SetCurrentLocation(containerViewModel.Container);
}
}
private async Task GoUp()
{
if (_currentLocation?.Parent is not AbsolutePath parentPath ||
if (_currentLocation?.Value?.Parent is not AbsolutePath parentPath ||
await parentPath.ResolveAsyncSafe() is not IContainer newContainer)
{
return;
}
_appState.RapidTravelText = "";
_selectedTab?.Tab?.SetCurrentLocation(newContainer);
}
private Task MoveCursorDown()
{
SelectNewSelectedItem(items =>
items.SkipWhile(i => !i.EqualsTo(_currentSelectedItem)).Skip(1).FirstOrDefault());
return Task.CompletedTask;
}
private Task MoveCursorUp()
{
SelectNewSelectedItem(items => items.TakeWhile(i => !i.EqualsTo(_currentSelectedItem)).LastOrDefault());
return Task.CompletedTask;
}
private Task MoveCursorDownPage()
{
SelectNewSelectedItem(items =>
if (_selectedTab?.Tab is { } tab)
{
var relevantItems = items.SkipWhile(i => !i.EqualsTo(_currentSelectedItem)).ToList();
await tab.SetCurrentLocation(newContainer);
}
}
private async Task MoveCursorDown()
=> await SelectNewSelectedItem(items =>
{
if (_currentSelectedItem?.Value == null) return items.FirstOrDefault();
return items.SkipWhile(i => !i.EqualsTo(_currentSelectedItem?.Value)).Skip(1).FirstOrDefault();
});
private async Task MoveCursorUp()
=> await SelectNewSelectedItem(items =>
{
if (_currentSelectedItem?.Value == null) return items.LastOrDefault();
return items.TakeWhile(i => !i.EqualsTo(_currentSelectedItem?.Value)).LastOrDefault();
});
private async Task MoveCursorDownPage()
=> await SelectNewSelectedItem(items =>
{
var relevantItems = _currentSelectedItem?.Value is null
? items.ToList()
: items.SkipWhile(i => !i.EqualsTo(_currentSelectedItem.Value)).ToList();
var fallBackItems = relevantItems.Take(PageSize + 1).Reverse();
var preferredItems = relevantItems.Skip(PageSize + 1);
return preferredItems.Concat(fallBackItems).FirstOrDefault();
});
return Task.CompletedTask;
}
private Task MoveCursorUpPage()
{
SelectNewSelectedItem(items =>
private async Task MoveCursorUpPage()
=> await SelectNewSelectedItem(items =>
{
var relevantItems = items.TakeWhile(i => !i.EqualsTo(_currentSelectedItem)).Reverse().ToList();
var relevantItems = _currentSelectedItem?.Value is null
? items.Reverse().ToList()
: items.TakeWhile(i => !i.EqualsTo(_currentSelectedItem?.Value)).Reverse().ToList();
var fallBackItems = relevantItems.Take(PageSize).Reverse();
var preferredItems = relevantItems.Skip(PageSize);
return preferredItems.Concat(fallBackItems).FirstOrDefault();
});
return Task.CompletedTask;
}
private Task MoveCursorToFirst()
private async Task MoveCursorToFirst()
=> await SelectNewSelectedItem(items => items.FirstOrDefault());
private async Task MoveCursorToLast()
=> await SelectNewSelectedItem(items => items.LastOrDefault());
private Task SelectNewSelectedItem(Func<IEnumerable<IItemViewModel>, IItemViewModel?> getNewSelected)
{
SelectNewSelectedItem(items => items.FirstOrDefault());
if (_selectedTab is null || _currentItems?.Value is null) return Task.CompletedTask;
var newSelectedItem = getNewSelected(_currentItems.Value);
if (newSelectedItem == null) return Task.CompletedTask;
if (_selectedTab.Tab is { } tab)
{
tab.SetSelectedItem(newSelectedItem.ToAbsolutePath(_timelessContentProvider));
}
return Task.CompletedTask;
}
private Task MoveCursorToLast()
{
SelectNewSelectedItem(items => items.LastOrDefault());
return Task.CompletedTask;
}
private void SelectNewSelectedItem(Func<IEnumerable<IItemViewModel>, IItemViewModel?> getNewSelected)
{
if (_selectedTab is null || _currentLocation is null) return;
var newSelectedItem = getNewSelected(_currentItems);
if (newSelectedItem == null) return;
_selectedTab.Tab?.SetSelectedItem(newSelectedItem.ToAbsolutePath(_timelessContentProvider));
}
private Task EnterRapidTravel()
{
_appState.SwitchViewMode(ViewMode.RapidTravel);
@@ -257,7 +272,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
return Task.CompletedTask;
}
private Task SwitchToTab(SwitchToTabCommand command)
private async Task SwitchToTab(SwitchToTabCommand command)
{
var number = command.TabNumber;
var tabViewModel = _appState.Tabs.FirstOrDefault(t => t.TabNumber == number);
@@ -269,8 +284,8 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
}
else if (tabViewModel == null)
{
var tab = _serviceProvider.GetInitableResolver<IContainer>(_currentLocation ?? _localContentProvider)
.GetRequiredService<ITab>();
var tab = await _serviceProvider.GetAsyncInitableResolver<IContainer>(_currentLocation?.Value ?? _localContentProvider)
.GetRequiredServiceAsync<ITab>();
var newTabViewModel = _serviceProvider.GetInitableResolver(tab, number).GetRequiredService<ITabViewModel>();
_appState.AddTab(newTabViewModel);
@@ -279,12 +294,10 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
if (_viewMode == ViewMode.RapidTravel)
{
_userCommandHandlerService.HandleCommandAsync(ExitRapidTravelCommand.Instance);
await _userCommandHandlerService.HandleCommandAsync(ExitRapidTravelCommand.Instance);
}
_appState.SetSelectedTab(tabViewModel!);
return Task.CompletedTask;
}
private Task CloseTab()

View File

@@ -1,4 +1,5 @@
using System.Diagnostics;
using DeclarativeProperty;
using FileTime.App.Core.UserCommand;
using FileTime.App.Core.ViewModels;
using FileTime.App.Search;
@@ -19,8 +20,8 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
private readonly ITimelessContentProvider _timelessContentProvider;
private readonly IUserCommandHandlerService _userCommandHandlerService;
private readonly IContentAccessorFactory _contentAccessorFactory;
private IContainer? _currentLocation;
private IItemViewModel? _currentSelectedItem;
private IDeclarativeProperty<IContainer?>? _currentLocation;
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
public ToolUserCommandHandlerService(
IAppState appState,
@@ -53,7 +54,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
private async Task CopyBase64()
{
var item = _currentSelectedItem?.BaseItem;
var item = _currentSelectedItem?.Value?.BaseItem;
if (item?.Type != AbsolutePathType.Element || item is not IElement element) return;
var contentReader = await _contentAccessorFactory.GetContentReaderFactory(element.Provider).CreateContentReaderAsync(element);
@@ -72,7 +73,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
private async Task Search(SearchCommand searchCommand)
{
if (_currentLocation is null) return;
if (_currentLocation?.Value is null) return;
var searchQuery = searchCommand.SearchText;
if (string.IsNullOrEmpty(searchQuery))
@@ -102,21 +103,21 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
_ => throw new ArgumentOutOfRangeException()
};
var searchTask = await _searchManager.StartSearchAsync(searchMatcher, _currentLocation);
var searchTask = await _searchManager.StartSearchAsync(searchMatcher, _currentLocation.Value);
var openContainerCommand = new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, searchTask.SearchContainer));
await _userCommandHandlerService.HandleCommandAsync(openContainerCommand);
}
private async Task CopyNativePath()
{
if (_currentSelectedItem?.BaseItem?.NativePath is null) return;
await _systemClipboardService.CopyToClipboardAsync(_currentSelectedItem.BaseItem.NativePath.Path);
if (_currentSelectedItem?.Value?.BaseItem?.NativePath is null) return;
await _systemClipboardService.CopyToClipboardAsync(_currentSelectedItem.Value.BaseItem.NativePath.Path);
}
private Task OpenInDefaultFileExplorer()
{
if (_currentLocation?.NativePath is null) return Task.CompletedTask;
Process.Start("explorer.exe", "\"" + _currentLocation.NativePath.Path + "\"");
if (_currentLocation?.Value?.NativePath is null) return Task.CompletedTask;
Process.Start("explorer.exe", "\"" + _currentLocation.Value.NativePath.Path + "\"");
return Task.CompletedTask;
}
}

View File

@@ -1,4 +1,6 @@
using System.Collections.ObjectModel;
using System.Reactive.Linq;
using DeclarativeProperty;
using DynamicData;
using FileTime.App.Core.ViewModels;
using FileTime.Core.Models;
@@ -44,14 +46,14 @@ public abstract class UserCommandHandlerServiceBase : IUserCommandHandler
protected IDisposable SaveSelectedTab(Action<ITabViewModel?> handler) => RunWithAppState(appState => appState.SelectedTab.Subscribe(handler));
protected IDisposable SaveCurrentSelectedItem(Action<IItemViewModel?> handler)
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t == null ? Observable.Return<IItemViewModel?>(null) : t.CurrentSelectedItem).Switch().Subscribe(handler));
protected IDisposable SaveCurrentSelectedItem(Action<IDeclarativeProperty<IItemViewModel?>?> handler)
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t?.CurrentSelectedItem).Subscribe(handler));
protected IDisposable SaveCurrentLocation(Action<IContainer?> handler)
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t == null ? Observable.Return<IContainer?>(null) : t.CurrentLocation).Switch().Subscribe(handler));
protected IDisposable SaveCurrentLocation(Action<IDeclarativeProperty<IContainer?>?> handler)
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t?.CurrentLocation).Subscribe(handler));
protected IDisposable SaveCurrentItems(Action<IEnumerable<IItemViewModel>> handler)
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t?.CurrentItemsCollectionObservable ?? Observable.Return((IEnumerable<IItemViewModel>?) Enumerable.Empty<IItemViewModel>())).Switch().Subscribe(i => handler(i ?? Enumerable.Empty<IItemViewModel>())));
protected IDisposable SaveCurrentItems(Action<IDeclarativeProperty<ObservableCollection<IItemViewModel>?>?> handler)
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t?.CurrentItems).Subscribe(handler));
protected IDisposable SaveMarkedItems(Action<IChangeSet<FullName>> handler)
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t == null ? Observable.Empty<IChangeSet<FullName>>() : t.MarkedItems).Switch().Subscribe(handler));