From 518cc350ad5dca6994e8a0228eb0d9e74ca51b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Kov=C3=A1cs?= Date: Tue, 24 May 2022 19:16:37 +0200 Subject: [PATCH] Error handling, better path remembrance --- .../ViewModels/ItemViewModel.cs | 34 +++++++++++++++---- .../Services/ITab.cs | 1 + src/Core/FileTime.Core.Services/Tab.cs | 22 ++++++------ .../Services/LifecycleService.cs | 26 ++++++++++++-- .../ViewModels/MainWindowViewModel.cs | 12 +------ 5 files changed, 64 insertions(+), 31 deletions(-) diff --git a/src/AppCommon/FileTime.App.Core/ViewModels/ItemViewModel.cs b/src/AppCommon/FileTime.App.Core/ViewModels/ItemViewModel.cs index c21b8af..d8e07b8 100644 --- a/src/AppCommon/FileTime.App.Core/ViewModels/ItemViewModel.cs +++ b/src/AppCommon/FileTime.App.Core/ViewModels/ItemViewModel.cs @@ -4,6 +4,7 @@ using DynamicData; using FileTime.App.Core.Models; using FileTime.App.Core.Models.Enums; using FileTime.App.Core.Services; +using FileTime.Core.Helper; using FileTime.Core.Models; using MoreLinq; using MvvmGen; @@ -15,6 +16,8 @@ namespace FileTime.App.Core.ViewModels; [Inject(typeof(IItemNameConverterService), "_itemNameConverterService")] public abstract partial class ItemViewModel : IItemViewModel { + private ITabViewModel? _parentTab; + [Property] private IItem? _baseItem; @@ -44,6 +47,8 @@ public abstract partial class ItemViewModel : IItemViewModel public void Init(IItem item, ITabViewModel parentTab, ItemViewModelType itemViewModelType) { + _parentTab = parentTab; + var sourceCollection = itemViewModelType switch { ItemViewModelType.Main => parentTab.CurrentItemsCollectionObservable, @@ -51,21 +56,21 @@ public abstract partial class ItemViewModel : IItemViewModel ItemViewModelType.SelectedChild => parentTab.SelectedsChildrenCollectionObservable, _ => throw new InvalidEnumArgumentException() }; - + BaseItem = item; DisplayName = _appState.SearchText.Select(s => _itemNameConverterService.GetDisplayName(item.DisplayName, s)); DisplayNameText = item.DisplayName; - - IsMarked = itemViewModelType is ItemViewModelType.Main + + IsMarked = itemViewModelType is ItemViewModelType.Main ? parentTab.MarkedItems.ToCollection().Select(m => m.Any(i => i.Path == item.FullName?.Path)) : Observable.Return(false); - + IsSelected = itemViewModelType is ItemViewModelType.Main ? parentTab.CurrentSelectedItem.Select(EqualsTo) - : Observable.Return(false); - + : Observable.Return(IsInDeespestPath()); + IsAlternative = sourceCollection.Select(c => c?.Index().FirstOrDefault(i => EqualsTo(i.Value)).Key % 2 == 0); - + ViewMode = Observable.CombineLatest(IsMarked, IsSelected, IsAlternative, GenerateViewMode).Throttle(TimeSpan.FromMilliseconds(10)); Attributes = item.Attributes; CreatedAt = item.CreatedAt; @@ -86,4 +91,19 @@ public abstract partial class ItemViewModel : IItemViewModel { return BaseItem?.FullName?.Path is string path && path == itemViewModel?.BaseItem?.FullName?.Path; } + + private bool IsInDeespestPath() + { + if (_parentTab?.Tab?.LastDeepestSelectedPath is null + || BaseItem?.FullName is null) + { + return false; + } + + var ownFullName = BaseItem.FullName; + var deepestPath = _parentTab.Tab.LastDeepestSelectedPath; + var commonPath = new FullName(PathHelper.GetCommonPath(ownFullName.Path, deepestPath.Path)); + + return commonPath.Path == ownFullName.Path; + } } \ No newline at end of file diff --git a/src/Core/FileTime.Core.Abstraction/Services/ITab.cs b/src/Core/FileTime.Core.Abstraction/Services/ITab.cs index 28d9f79..11bdb8d 100644 --- a/src/Core/FileTime.Core.Abstraction/Services/ITab.cs +++ b/src/Core/FileTime.Core.Abstraction/Services/ITab.cs @@ -9,6 +9,7 @@ public interface ITab : IInitable, IDisposable IObservable CurrentLocation { get; } IObservable CurrentSelectedItem { get; } IObservable>?> CurrentItems { get; } + FullName? LastDeepestSelectedPath { get; } void SetCurrentLocation(IContainer newLocation); void AddItemFilter(ItemFilter filter); diff --git a/src/Core/FileTime.Core.Services/Tab.cs b/src/Core/FileTime.Core.Services/Tab.cs index 8572764..6bf9259 100644 --- a/src/Core/FileTime.Core.Services/Tab.cs +++ b/src/Core/FileTime.Core.Services/Tab.cs @@ -15,13 +15,13 @@ public class Tab : ITab private readonly BehaviorSubject _currentLocationForced = new(null); private readonly BehaviorSubject _currentSelectedItem = new(null); private readonly SourceList _itemFilters = new(); - private FullName? _lastDeepestSelected; private AbsolutePath? _currentSelectedItemCached; private PointInTime _currentPointInTime; public IObservable CurrentLocation { get; } public IObservable>?> CurrentItems { get; } public IObservable CurrentSelectedItem { get; } + public FullName? LastDeepestSelectedPath { get; private set; } public Tab(ITimelessContentProvider timelessContentProvider) { @@ -33,6 +33,13 @@ public class Tab : ITab CurrentLocation = _currentLocation .DistinctUntilChanged() .Merge(_currentLocationForced) + .Do(_ => + { + if (_currentSelectedItemCached is not null) + { + LastDeepestSelectedPath = new FullName(PathHelper.GetLongerPath(LastDeepestSelectedPath?.Path, _currentSelectedItemCached.Path.Path)); + } + }) .Publish(null) .RefCount(); @@ -79,11 +86,6 @@ public class Tab : ITab { _currentSelectedItemCached = s; _currentSelectedItem.OnNext(s); - - if (s is not null) - { - _lastDeepestSelected = new FullName(PathHelper.GetLongerPath(_lastDeepestSelected?.Path, s.Path.Path)); - } }); } @@ -99,13 +101,13 @@ public class Tab : ITab if (!items.Any()) return null; var newSelectedItem = new AbsolutePath(_timelessContentProvider, items.First()); - if (_lastDeepestSelected is not null) + if (LastDeepestSelectedPath is not null) { var parentPath = items.First().FullName?.GetParent()?.Path; - if (parentPath is not null && _lastDeepestSelected.Path.StartsWith(parentPath)) + if (parentPath is not null && LastDeepestSelectedPath.Path.StartsWith(parentPath)) { - var itemNameToSelect = _lastDeepestSelected.Path + var itemNameToSelect = LastDeepestSelectedPath.Path .Split(Constants.SeparatorChar) .Skip((parentPath?.Split(Constants.SeparatorChar).Length) ?? 0) .FirstOrDefault(); @@ -119,7 +121,7 @@ public class Tab : ITab } } - _lastDeepestSelected = new FullName(PathHelper.GetLongerPath(_lastDeepestSelected?.Path, newSelectedItem.Path.Path)); + LastDeepestSelectedPath = new FullName(PathHelper.GetLongerPath(LastDeepestSelectedPath?.Path, newSelectedItem.Path.Path)); return newSelectedItem; } diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Services/LifecycleService.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Services/LifecycleService.cs index ae1be7b..341cf86 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/Services/LifecycleService.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Services/LifecycleService.cs @@ -1,4 +1,5 @@ using FileTime.App.Core.Services; +using Microsoft.Extensions.Logging; namespace FileTime.GuiApp.Services; @@ -6,18 +7,30 @@ public class LifecycleService { private readonly IEnumerable _exitHandlers; private readonly IEnumerable _startupHandlers; + private readonly ILogger _logger; - public LifecycleService(IEnumerable startupHandlers, IEnumerable exitHandlers) + public LifecycleService( + IEnumerable startupHandlers, + IEnumerable exitHandlers, + ILogger logger) { _exitHandlers = exitHandlers; _startupHandlers = startupHandlers; + _logger = logger; } public async Task InitStartupHandlersAsync() { foreach (var startupHandler in _startupHandlers) { - await startupHandler.InitAsync(); + try + { + await startupHandler.InitAsync(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error while running startup handler {handler}", startupHandler?.GetType().FullName); + } } } @@ -25,7 +38,14 @@ public class LifecycleService { foreach (var exitHandler in _exitHandlers) { - await exitHandler.ExitAsync(); + try + { + await exitHandler.ExitAsync(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error while running exit handler {handler}", exitHandler?.GetType().FullName); + } } } } \ No newline at end of file diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/ViewModels/MainWindowViewModel.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/ViewModels/MainWindowViewModel.cs index b3b07d7..bfd9870 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/ViewModels/MainWindowViewModel.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/ViewModels/MainWindowViewModel.cs @@ -49,17 +49,7 @@ public partial class MainWindowViewModel : IMainWindowViewModelBase Title = "FileTime " + versionString; - //TODO: refactor - /*if (AppState.Tabs.Count == 0) - { - var tab = _serviceProvider.GetInitableResolver(_localContentProvider) - .GetRequiredService(); - var tabViewModel = _serviceProvider.GetInitableResolver(tab, 1).GetRequiredService(); - - _appState.AddTab(tabViewModel); - }*/ - - _lifecycleService.InitStartupHandlersAsync().Wait(); + Task.Run(async () => await _lifecycleService.InitStartupHandlersAsync()).Wait(); } public void ProcessKeyDown(Key key, KeyModifiers keyModifiers, Action setHandled)