diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/ViewModels/IAppState.cs b/src/AppCommon/FileTime.App.Core.Abstraction/ViewModels/IAppState.cs index 82f4030..deef171 100644 --- a/src/AppCommon/FileTime.App.Core.Abstraction/ViewModels/IAppState.cs +++ b/src/AppCommon/FileTime.App.Core.Abstraction/ViewModels/IAppState.cs @@ -8,16 +8,15 @@ namespace FileTime.App.Core.ViewModels; public interface IAppState { ReadOnlyObservableCollection Tabs { get; } - IObservable SelectedTab { get; } + IDeclarativeProperty SelectedTab { get; } IObservable SearchText { get; } IDeclarativeProperty ViewMode { get; } DeclarativeProperty RapidTravelText { get; } - ITabViewModel? CurrentSelectedTab { get; } ITimelineViewModel TimelineViewModel { get; } void AddTab(ITabViewModel tabViewModel); void RemoveTab(ITabViewModel tabViewModel); void SetSearchText(string? searchText); Task SwitchViewModeAsync(ViewMode newViewMode); - void SetSelectedTab(ITabViewModel tabToSelect); + Task SetSelectedTabAsync(ITabViewModel tabToSelect); } \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core/Services/Persistence/TabPersistenceService.cs b/src/AppCommon/FileTime.App.Core/Services/Persistence/TabPersistenceService.cs index 17da0a2..65277d2 100644 --- a/src/AppCommon/FileTime.App.Core/Services/Persistence/TabPersistenceService.cs +++ b/src/AppCommon/FileTime.App.Core/Services/Persistence/TabPersistenceService.cs @@ -177,7 +177,7 @@ public class TabPersistenceService : ITabPersistenceService .SkipWhile(t => t.TabNumber <= tabStates.ActiveTabNumber); var tabToActivate = optimalTabs.Concat(suboptimalTabs).FirstOrDefault(); - if (tabToActivate is not null) _appState.SetSelectedTab(tabToActivate); + if (tabToActivate is not null) await _appState.SetSelectedTabAsync(tabToActivate); return true; } @@ -205,7 +205,7 @@ public class TabPersistenceService : ITabPersistenceService return new TabStates( tabStates, - _appState.CurrentSelectedTab?.TabNumber + _appState.SelectedTab.Value?.TabNumber ); } diff --git a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/NavigationUserCommandHandlerService.cs b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/NavigationUserCommandHandlerService.cs index 054774c..9afb8e3 100644 --- a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/NavigationUserCommandHandlerService.cs +++ b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/NavigationUserCommandHandlerService.cs @@ -345,7 +345,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase await _userCommandHandlerService.HandleCommandAsync(ExitRapidTravelCommand.Instance); } - _appState.SetSelectedTab(tabViewModel!); + await _appState.SetSelectedTabAsync(tabViewModel!); } private Task CloseTab() diff --git a/src/AppCommon/FileTime.App.Core/ViewModels/AppStateBase.cs b/src/AppCommon/FileTime.App.Core/ViewModels/AppStateBase.cs index a949951..81f1f28 100644 --- a/src/AppCommon/FileTime.App.Core/ViewModels/AppStateBase.cs +++ b/src/AppCommon/FileTime.App.Core/ViewModels/AppStateBase.cs @@ -15,17 +15,16 @@ namespace FileTime.App.Core.ViewModels; public abstract partial class AppStateBase : IAppState { private readonly BehaviorSubject _searchText = new(null); - private readonly BehaviorSubject _selectedTab = new(null); + private readonly DeclarativeProperty _selectedTab = new(); private readonly DeclarativeProperty _viewMode = new(Models.Enums.ViewMode.Default); - private readonly SourceList _tabs = new(); + private readonly ObservableCollection _tabs = new(); public IDeclarativeProperty ViewMode { get; private set; } public ReadOnlyObservableCollection Tabs { get; private set; } public IObservable SearchText { get; private set; } - public IObservable SelectedTab { get; private set; } - public ITabViewModel? CurrentSelectedTab { get; private set; } + public IDeclarativeProperty SelectedTab { get; private set; } public DeclarativeProperty RapidTravelText { get; private set; } partial void OnInitialize() @@ -33,37 +32,28 @@ public abstract partial class AppStateBase : IAppState RapidTravelText = new(""); ViewMode = _viewMode; - var tabsObservable = _tabs.Connect(); - SearchText = _searchText.AsObservable(); - SelectedTab = Observable.CombineLatest(tabsObservable.ToCollection(), _selectedTab.DistinctUntilChanged(), GetSelectedTab); + SelectedTab = DeclarativePropertyHelpers.CombineLatest, ITabViewModel, ITabViewModel>( + _tabs.Watch(), + _selectedTab, + (tabs, selectedTab) => Task.FromResult(GetSelectedTab(tabs, selectedTab)) + ); - SelectedTab.Subscribe(t => - { - _selectedTab.OnNext(t); - CurrentSelectedTab = t; - }); - - tabsObservable - .Bind(out var collection) - .DisposeMany() - .Subscribe(); - - Tabs = collection; + Tabs = new ReadOnlyObservableCollection(_tabs); } public void AddTab(ITabViewModel tabViewModel) { - if (_tabs.Items.Any(t => t.TabNumber == tabViewModel.TabNumber)) + if (_tabs.Any(t => t.TabNumber == tabViewModel.TabNumber)) throw new ArgumentException($"There is a tab with the same tab number {tabViewModel.TabNumber}.", nameof(tabViewModel)); - var index = _tabs.Items.Count(t => t.TabNumber < tabViewModel.TabNumber); + var index = _tabs.Count(t => t.TabNumber < tabViewModel.TabNumber); _tabs.Insert(index, tabViewModel); } public void RemoveTab(ITabViewModel tabViewModel) { - if (!_tabs.Items.Contains(tabViewModel)) return; + if (!_tabs.Contains(tabViewModel)) return; _tabs.Remove(tabViewModel); } @@ -72,7 +62,7 @@ public abstract partial class AppStateBase : IAppState public async Task SwitchViewModeAsync(ViewMode newViewMode) => await _viewMode.SetValue(newViewMode); - public void SetSelectedTab(ITabViewModel tabToSelect) => _selectedTab.OnNext(tabToSelect); + public async Task SetSelectedTabAsync(ITabViewModel tabToSelect) => await _selectedTab.SetValue(tabToSelect); private ITabViewModel? GetSelectedTab(IEnumerable tabs, ITabViewModel? expectedSelectedTab) { diff --git a/src/AppCommon/FileTime.App.Core/ViewModels/ItemViewModel.cs b/src/AppCommon/FileTime.App.Core/ViewModels/ItemViewModel.cs index f3b0973..bde6934 100644 --- a/src/AppCommon/FileTime.App.Core/ViewModels/ItemViewModel.cs +++ b/src/AppCommon/FileTime.App.Core/ViewModels/ItemViewModel.cs @@ -56,7 +56,7 @@ public abstract partial class ItemViewModel : IItemViewModel { ItemViewModelType.Main => _appState.RapidTravelText.Map(async (s, _) => _appState.ViewMode.Value != Models.Enums.ViewMode.RapidTravel - && _appState.CurrentSelectedTab?.CurrentLocation.Value?.Provider is IItemNameConverterProvider nameConverterProvider + && _appState.SelectedTab.Value?.CurrentLocation.Value?.Provider is IItemNameConverterProvider nameConverterProvider ? (IReadOnlyList) await nameConverterProvider.GetItemNamePartsAsync(item) : _itemNameConverterService.GetDisplayName(item.DisplayName, s) ), diff --git a/src/Library/DeclarativeProperty/CollectionRepeaterProperty.cs b/src/Library/DeclarativeProperty/CollectionRepeaterProperty.cs index 958d9e5..75ab187 100644 --- a/src/Library/DeclarativeProperty/CollectionRepeaterProperty.cs +++ b/src/Library/DeclarativeProperty/CollectionRepeaterProperty.cs @@ -7,16 +7,25 @@ public class CollectionRepeaterProperty : DeclarativePropert { private TCollection? _currentCollection; - public CollectionRepeaterProperty(IDeclarativeProperty from) + public CollectionRepeaterProperty(IDeclarativeProperty from) : base(from.Value) { _currentCollection = from.Value; if (from.Value is { } value) { value.CollectionChanged += HandleCollectionChanged; } + AddDisposable(from.Subscribe(Handle)); } + public CollectionRepeaterProperty(TCollection? collection) : base(collection) + { + ArgumentNullException.ThrowIfNull(collection); + + _currentCollection = collection; + collection.CollectionChanged += HandleCollectionChanged; + } + private void HandleCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { var t = Task.Run(async () => await NotifySubscribersAsync(Value)); @@ -29,7 +38,8 @@ public class CollectionRepeaterProperty : DeclarativePropert { currentCollection.CollectionChanged -= HandleCollectionChanged; } - if (collection is {} newCollection) + + if (collection is { } newCollection) { newCollection.CollectionChanged -= HandleCollectionChanged; newCollection.CollectionChanged += HandleCollectionChanged; diff --git a/src/Library/DeclarativeProperty/DeclarativePropertyExtensions.cs b/src/Library/DeclarativeProperty/DeclarativePropertyExtensions.cs index 99dc15a..1bfdffc 100644 --- a/src/Library/DeclarativeProperty/DeclarativePropertyExtensions.cs +++ b/src/Library/DeclarativeProperty/DeclarativePropertyExtensions.cs @@ -69,6 +69,19 @@ public static class DeclarativePropertyExtensions this IDeclarativeProperty collection) where TCollection : IList, INotifyCollectionChanged => new CollectionRepeaterProperty(collection); + + public static IDeclarativeProperty Watch( + this TCollection collection) + where TCollection : IList, INotifyCollectionChanged + => new CollectionRepeaterProperty(collection); + + public static IDeclarativeProperty?> Watch( + this ObservableCollection collection) + => new CollectionRepeaterProperty?, TItem>(collection); + + public static IDeclarativeProperty?> Watch( + this ReadOnlyObservableCollection collection) + => new CollectionRepeaterProperty?, TItem>(collection); public static IDeclarativeProperty CombineLatest(