diff --git a/src/AppCommon/FileTime.App.ContainerSizeScanner/SizeScanContainer.cs b/src/AppCommon/FileTime.App.ContainerSizeScanner/SizeScanContainer.cs index 5c9f68c..60708cb 100644 --- a/src/AppCommon/FileTime.App.ContainerSizeScanner/SizeScanContainer.cs +++ b/src/AppCommon/FileTime.App.ContainerSizeScanner/SizeScanContainer.cs @@ -88,7 +88,7 @@ public record SizeScanContainer : ISizeScanContainer return Task.CompletedTask; } - public required IDeclarativeProperty Status { get; init; } = new DeclarativeProperty(); + public required IDeclarativeProperty Status { get; init; } = new DeclarativeProperty(string.Empty); public required SizeScanTask SizeScanTask { get; init; } diff --git a/src/AppCommon/FileTime.App.ContainerSizeScanner/SizeScanTask.cs b/src/AppCommon/FileTime.App.ContainerSizeScanner/SizeScanTask.cs index 45c2538..9163590 100644 --- a/src/AppCommon/FileTime.App.ContainerSizeScanner/SizeScanTask.cs +++ b/src/AppCommon/FileTime.App.ContainerSizeScanner/SizeScanTask.cs @@ -17,7 +17,7 @@ public class SizeScanTask : ISizeScanTask private readonly ILogger _logger; private Thread? _sizeScanThread; private static int _searchId = 1; - private readonly DeclarativeProperty _containerStatus = new(); + private readonly DeclarativeProperty _containerStatus = new(string.Empty); private readonly IDeclarativeProperty _containerStatusDebounced; public ISizeScanContainer SizeSizeScanContainer { get; private set; } = null!; public bool IsRunning { get; private set; } diff --git a/src/AppCommon/FileTime.App.Core/ViewModels/AppStateBase.cs b/src/AppCommon/FileTime.App.Core/ViewModels/AppStateBase.cs index 83b4e02..aafb0b7 100644 --- a/src/AppCommon/FileTime.App.Core/ViewModels/AppStateBase.cs +++ b/src/AppCommon/FileTime.App.Core/ViewModels/AppStateBase.cs @@ -13,7 +13,7 @@ namespace FileTime.App.Core.ViewModels; public abstract partial class AppStateBase : IAppState { private readonly BehaviorSubject _searchText = new(null); - private readonly DeclarativeProperty _selectedTab = new(); + private readonly DeclarativeProperty _selectedTab = new(null); private readonly DeclarativeProperty _viewMode = new(Models.Enums.ViewMode.Default); private readonly ObservableCollection _tabs = new(); private readonly DeclarativeProperty _rapidTravelText; diff --git a/src/Core/FileTime.Core.Command/Copy/CopyCommand.cs b/src/Core/FileTime.Core.Command/Copy/CopyCommand.cs index d011645..5916a56 100644 --- a/src/Core/FileTime.Core.Command/Copy/CopyCommand.cs +++ b/src/Core/FileTime.Core.Command/Copy/CopyCommand.cs @@ -15,11 +15,11 @@ public class CopyCommand : CommandBase, ITransportationCommand private readonly CancellationTokenSource _cancellationTokenSource = new(); private readonly List _operationProgresses = new(); - private readonly DeclarativeProperty _currentOperationProgress = new(); + private readonly DeclarativeProperty _currentOperationProgress = new(null); private long _recentTotalSum; - private readonly DeclarativeProperty _recentTotalProcessed = new(); - private readonly DeclarativeProperty _recentStartTime = new(); + private readonly DeclarativeProperty _recentTotalProcessed = new(0); + private readonly DeclarativeProperty _recentStartTime = new(DateTime.Now); public IReadOnlyList Sources { get; } diff --git a/src/Core/FileTime.Core.Services/Tab.cs b/src/Core/FileTime.Core.Services/Tab.cs index 8ecca8b..be27275 100644 --- a/src/Core/FileTime.Core.Services/Tab.cs +++ b/src/Core/FileTime.Core.Services/Tab.cs @@ -19,9 +19,9 @@ public class Tab : ITab private readonly ITimelessContentProvider _timelessContentProvider; private readonly ITabEvents _tabEvents; - private readonly DeclarativeProperty _currentLocation = new(); - private readonly DeclarativeProperty _currentLocationForced = new(); - private readonly DeclarativeProperty _currentRequestItem = new(); + private readonly DeclarativeProperty _currentLocation = new(null); + private readonly DeclarativeProperty _currentLocationForced = new(null); + private readonly DeclarativeProperty _currentRequestItem = new(null); private readonly ObservableCollection _itemFilters = new(); private readonly CircularBuffer _history = new(20); private readonly CircularBuffer _future = new(20); @@ -64,28 +64,26 @@ public class Tab : ITab return Task.CompletedTask; }); - CurrentItems = DeclarativePropertyHelpers.CombineLatest( + IDeclarativeProperty?> asd1 = DeclarativePropertyHelpers.CombineLatest( CurrentLocation, itemFiltersProperty, - (container, filters) => + Task?> (container, filters) => { ObservableCollection? items = null; - if (container is not null) - { - items = container - .Items - .Selecting(i => MapItem(i)); + if (container is null) return Task.FromResult(items); - if (filters is not null) - { - items = items.Filtering(i => filters.All(f => f.Filter(i))); - } - } + items = container + .Items + .Selecting(i => MapItem(i)); - return Task.FromResult(items); + items = items.Filtering(i => filters.All(f => f.Filter(i))); + + return Task.FromResult(items)!; } - ).CombineLatest( + ); + + var asd2 = asd1.CombineLatest( Ordering.Map(ordering => { var (itemComparer, order) = ordering switch @@ -103,7 +101,7 @@ public class Tab : ITab return (itemComparer, order, ordering is not ItemOrdering.Name and not ItemOrdering.NameDesc); }), - (items, ordering) => + Task?> (items, ordering) => { if (items is null) return Task.FromResult?>(null); @@ -117,14 +115,16 @@ public class Tab : ITab ? primaryOrderedItems.ThenOrdering(i => i.DisplayName) : primaryOrderedItems; - return Task.FromResult(orderedItems); + return Task.FromResult?>(orderedItems); } ); + CurrentItems = asd2; + CurrentSelectedItem = DeclarativePropertyHelpers.CombineLatest( - CurrentItems.Watch, IItem>(), + CurrentItems.Watch?, IItem>(), _currentRequestItem.DistinctUntilChanged(), - (items, selected) => + Task (items, selected) => { var itemSelectingContext = new LastItemSelectingContext(CurrentLocation.Value); var lastItemSelectingContext = _lastItemSelectingContext; @@ -143,7 +143,7 @@ public class Tab : ITab var candidate = _selectedItemCandidates.FirstOrDefault(c => items.Any(i => i.FullName?.Path == c.Path.Path)); if (candidate != null) { - return Task.FromResult(candidate); + return Task.FromResult(candidate); } } @@ -160,7 +160,7 @@ public class Tab : ITab DeclarativePropertyHelpers.CombineLatest( CurrentItems, CurrentSelectedItem, - (items, selected) => + Task?> (items, selected) => { if (items is null || selected is null) return Task.FromResult?>(null); var primaryCandidates = items.SkipWhile(i => i.FullName is {Path: var p} && p != selected.Path.Path).Skip(1); @@ -169,7 +169,7 @@ public class Tab : ITab .Concat(secondaryCandidates) .Select(c => new AbsolutePath(_timelessContentProvider, c)); - return Task.FromResult(candidates); + return Task.FromResult?>(candidates); }) .Subscribe(candidates => { diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.App/ViewModels/IMainWindowViewModelBase.cs b/src/GuiApp/Avalonia/FileTime.GuiApp.App/ViewModels/IMainWindowViewModelBase.cs index 094ec9b..7cac930 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp.App/ViewModels/IMainWindowViewModelBase.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.App/ViewModels/IMainWindowViewModelBase.cs @@ -8,6 +8,6 @@ public interface IMainWindowViewModelBase DeclarativeProperty Title { get; } bool Loading { get; } IObservable MainFont { get; } - DeclarativeProperty FatalError { get; } + DeclarativeProperty FatalError { get; } IReadOnlyList TransparencyLevelHint { get; } } \ No newline at end of file diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.App/ViewModels/MainWindowLoadingViewModel.cs b/src/GuiApp/Avalonia/FileTime.GuiApp.App/ViewModels/MainWindowLoadingViewModel.cs index ec6d8c4..e509585 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp.App/ViewModels/MainWindowLoadingViewModel.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.App/ViewModels/MainWindowLoadingViewModel.cs @@ -9,6 +9,6 @@ public class MainWindowLoadingViewModel : IMainWindowViewModelBase public bool Loading => true; public IObservable MainFont { get; } = new BehaviorSubject(""); public DeclarativeProperty Title { get; } = new("Loading..."); - public DeclarativeProperty FatalError { get; } = new(); + public DeclarativeProperty FatalError { get; } = new(null); public IReadOnlyList TransparencyLevelHint { get; } = new[] {WindowTransparencyLevel.AcrylicBlur}; } \ No newline at end of file diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.App/ViewModels/MainWindowViewModel.cs b/src/GuiApp/Avalonia/FileTime.GuiApp.App/ViewModels/MainWindowViewModel.cs index 11752ea..491e198 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp.App/ViewModels/MainWindowViewModel.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.App/ViewModels/MainWindowViewModel.cs @@ -53,10 +53,10 @@ public partial class MainWindowViewModel : IMainWindowViewModel private readonly OcConsumer _rootDriveInfosConsumer = new(); public bool Loading => false; public IObservable MainFont => _fontService.MainFont.Select(x => x ?? ""); - public DeclarativeProperty FatalError { get; } = new(); + public DeclarativeProperty FatalError { get; } = new(null); public IReadOnlyList TransparencyLevelHint { get; } = new[] {WindowTransparencyLevel.Blur}; public IGuiAppState AppState => _appState; - public DeclarativeProperty Title { get; } = new(); + public DeclarativeProperty Title { get; } = new(string.Empty); public Thickness IconStatusPanelMargin { get; private set; } = new(20, 10, 10, 10); public Action? FocusDefaultElement { get; set; } public Action? ShowWindow { get; set; } diff --git a/src/Library/DeclarativeProperty/CollectionRepeaterProperty.cs b/src/Library/DeclarativeProperty/CollectionRepeaterProperty.cs index 75ab187..9aaccf2 100644 --- a/src/Library/DeclarativeProperty/CollectionRepeaterProperty.cs +++ b/src/Library/DeclarativeProperty/CollectionRepeaterProperty.cs @@ -3,11 +3,11 @@ namespace DeclarativeProperty; public class CollectionRepeaterProperty : DeclarativePropertyBase - where TCollection : IList, INotifyCollectionChanged + where TCollection : IList?, INotifyCollectionChanged? { private TCollection? _currentCollection; - public CollectionRepeaterProperty(IDeclarativeProperty from) : base(from.Value) + public CollectionRepeaterProperty(IDeclarativeProperty from) : base(from.Value) { _currentCollection = from.Value; if (from.Value is { } value) @@ -18,7 +18,7 @@ public class CollectionRepeaterProperty : DeclarativePropert AddDisposable(from.Subscribe(Handle)); } - public CollectionRepeaterProperty(TCollection? collection) : base(collection) + public CollectionRepeaterProperty(TCollection collection) : base(collection) { ArgumentNullException.ThrowIfNull(collection); @@ -32,7 +32,7 @@ public class CollectionRepeaterProperty : DeclarativePropert t.Wait(); } - private async Task Handle(TCollection? collection, CancellationToken cancellationToken = default) + private async Task Handle(TCollection collection, CancellationToken cancellationToken = default) { if (_currentCollection is { } currentCollection) { diff --git a/src/Library/DeclarativeProperty/CombineAllProperty.cs b/src/Library/DeclarativeProperty/CombineAllProperty.cs index 89236b0..ae0184d 100644 --- a/src/Library/DeclarativeProperty/CombineAllProperty.cs +++ b/src/Library/DeclarativeProperty/CombineAllProperty.cs @@ -3,18 +3,18 @@ public class CombineAllProperty : DeclarativePropertyBase { private readonly List> _sources; - private readonly Func, Task> _combiner; + private readonly Func, Task> _combiner; public CombineAllProperty( IEnumerable> sources, - Func, Task> combiner, - Action? setValueHook = null) : base(setValueHook) + Func, Task> combiner, + Action? setValueHook = null) : base(default!, setValueHook) { var sourcesList = sources.ToList(); _sources = sourcesList; _combiner = combiner; - var initialValueTask = _combiner(sourcesList.Select(p => p.Value)!); + var initialValueTask = Task.Run(async () => await _combiner(sourcesList.Select(p => p.Value))); initialValueTask.Wait(); SetNewValueSync(initialValueTask.Result); @@ -24,12 +24,12 @@ public class CombineAllProperty : DeclarativePropertyBase } } - private async Task OnSourceChanged(T? arg1, CancellationToken arg2) => await Update(); + private async Task OnSourceChanged(T arg1, CancellationToken arg2) => await Update(); private async Task Update() { var values = _sources.Select(p => p.Value); - var result = await _combiner(values!); + var result = await _combiner(values); await SetNewValueAsync(result); } diff --git a/src/Library/DeclarativeProperty/CombineLatestProperty.cs b/src/Library/DeclarativeProperty/CombineLatestProperty.cs index a450209..03120da 100644 --- a/src/Library/DeclarativeProperty/CombineLatestProperty.cs +++ b/src/Library/DeclarativeProperty/CombineLatestProperty.cs @@ -2,23 +2,27 @@ public sealed class CombineLatestProperty : DeclarativePropertyBase { - private readonly Func> _func; - private T1? _value1; - private T2? _value2; + private readonly Func> _func; + private T1 _value1; + private T2 _value2; public CombineLatestProperty( IDeclarativeProperty prop1, IDeclarativeProperty prop2, - Func> func, - Action? setValueHook = null) : base(setValueHook) + Func> func, + Action? setValueHook = null) : base(default!, setValueHook) { ArgumentNullException.ThrowIfNull(prop1); ArgumentNullException.ThrowIfNull(prop2); _func = func; - _value1 = prop1.Value is null ? default : prop1.Value; - _value2 = prop2.Value is null ? default : prop2.Value; + _value1 = prop1.Value; + _value2 = prop2.Value; + + var initialValueTask = Task.Run(async () => await _func(_value1, _value2)); + initialValueTask.Wait(); + SetNewValueSync(initialValueTask.Result); prop1.Subscribe(async (value1, token) => { @@ -51,7 +55,7 @@ public sealed class CombineLatestProperty : DeclarativeProp IDeclarativeProperty prop2, IDeclarativeProperty prop3, Func> func, - Action? setValueHook = null) : base(setValueHook) + Action? setValueHook = null) : base(default!, setValueHook) { ArgumentNullException.ThrowIfNull(prop1); ArgumentNullException.ThrowIfNull(prop2); @@ -59,9 +63,13 @@ public sealed class CombineLatestProperty : DeclarativeProp _func = func; - _value1 = prop1.Value is null ? default : prop1.Value; - _value2 = prop2.Value is null ? default : prop2.Value; - _value3 = prop3.Value is null ? default : prop3.Value; + _value1 = prop1.Value; + _value2 = prop2.Value; + _value3 = prop3.Value; + + var initialValueTask = Task.Run(async () => await _func(_value1, _value2, _value3)); + initialValueTask.Wait(); + SetNewValueSync(initialValueTask.Result); prop1.Subscribe(async (value1, token) => { diff --git a/src/Library/DeclarativeProperty/CombineProperty.cs b/src/Library/DeclarativeProperty/CombineProperty.cs index 857e01e..5625cc1 100644 --- a/src/Library/DeclarativeProperty/CombineProperty.cs +++ b/src/Library/DeclarativeProperty/CombineProperty.cs @@ -5,7 +5,7 @@ public sealed class CombineProperty : DeclarativePropertyBase private readonly Func, Task> _combiner; private readonly List> _sourceProperties = new(); - public CombineProperty(Func, Task> combiner) + public CombineProperty(Func, Task> combiner) : base(default!) { _combiner = combiner; } diff --git a/src/Library/DeclarativeProperty/DebounceProperty.cs b/src/Library/DeclarativeProperty/DebounceProperty.cs index b26bd2d..51fb7a9 100644 --- a/src/Library/DeclarativeProperty/DebounceProperty.cs +++ b/src/Library/DeclarativeProperty/DebounceProperty.cs @@ -3,9 +3,9 @@ public sealed class DebounceProperty : DeclarativePropertyBase { private readonly object _lock = new(); - private readonly Func _interval; + private readonly Func _interval; private DateTime _startTime = DateTime.MinValue; - private T? _nextValue; + private T _nextValue = default!; private CancellationToken _nextCancellationToken; private bool _isThrottleTaskRunning; public bool ResetTimer { get; init; } @@ -13,14 +13,14 @@ public sealed class DebounceProperty : DeclarativePropertyBase public DebounceProperty( IDeclarativeProperty from, - Func interval, - Action? setValueHook = null) : base(from.Value, setValueHook) + Func interval, + Action? setValueHook = null) : base(from.Value, setValueHook) { _interval = interval; AddDisposable(from.Subscribe(SetValue)); } - private Task SetValue(T? next, CancellationToken cancellationToken = default) + private Task SetValue(T next, CancellationToken cancellationToken = default) { lock (_lock) { @@ -38,7 +38,7 @@ public sealed class DebounceProperty : DeclarativePropertyBase _startTime = DateTime.Now; _isThrottleTaskRunning = true; - Task.Run(async () => await StartDebounceTask()); + Task.Run(StartDebounceTask); } return Task.CompletedTask; @@ -51,7 +51,7 @@ public sealed class DebounceProperty : DeclarativePropertyBase await Task.Delay(WaitInterval); } - T? next; + T next; CancellationToken cancellationToken; lock (_lock) { diff --git a/src/Library/DeclarativeProperty/DeclarativeProperty.cs b/src/Library/DeclarativeProperty/DeclarativeProperty.cs index a101871..7e6bf39 100644 --- a/src/Library/DeclarativeProperty/DeclarativeProperty.cs +++ b/src/Library/DeclarativeProperty/DeclarativeProperty.cs @@ -6,15 +6,15 @@ public static class DeclarativePropertyHelpers IDeclarativeProperty prop1, IDeclarativeProperty prop2, Func> func, - Action? setValueHook = null) - => new(prop1, prop2, func!, setValueHook); + Action? setValueHook = null) + => new(prop1, prop2, func, setValueHook); public static CombineLatestProperty CombineLatest( IDeclarativeProperty prop1, IDeclarativeProperty prop2, IDeclarativeProperty prop3, Func> func, - Action? setValueHook = null) + Action? setValueHook = null) => new(prop1, prop2, prop3, func!, setValueHook); public static MergeProperty Merge(params IDeclarativeProperty[] props) @@ -23,11 +23,7 @@ public static class DeclarativePropertyHelpers public sealed class DeclarativeProperty : DeclarativePropertyBase { - public DeclarativeProperty(Action? setValueHook = null) : base(setValueHook) - { - } - - public DeclarativeProperty(T initialValue, Action? setValueHook = null) : base(initialValue, setValueHook) + public DeclarativeProperty(T initialValue, Action? setValueHook = null) : base(initialValue, setValueHook) { } diff --git a/src/Library/DeclarativeProperty/DeclarativePropertyBase.cs b/src/Library/DeclarativeProperty/DeclarativePropertyBase.cs index b7b9878..9d2a703 100644 --- a/src/Library/DeclarativeProperty/DeclarativePropertyBase.cs +++ b/src/Library/DeclarativeProperty/DeclarativePropertyBase.cs @@ -5,8 +5,8 @@ namespace DeclarativeProperty; public abstract class DeclarativePropertyBase : IDeclarativeProperty { - private readonly List> _subscribers = new(); - private readonly Action? _setValueHook; + private readonly List> _subscribers = new(); + private readonly Action? _setValueHook; private readonly List _disposables = new(); private readonly List, T, IDisposable?>> _subscribeTriggers = new(); private readonly List, T>> _unsubscribeTriggers = new(); @@ -14,29 +14,24 @@ public abstract class DeclarativePropertyBase : IDeclarativeProperty private readonly object _triggerLock = new(); private readonly object _subscriberLock = new(); - private T? _value; + private T _value; - public T? Value + public T Value { get => _value; set => _setValueHook?.Invoke(value); } - protected DeclarativePropertyBase(Action? setValueHook = null) - { - _setValueHook = setValueHook; - } - - protected DeclarativePropertyBase(T? initialValue, Action? setValueHook = null) + protected DeclarativePropertyBase(T initialValue, Action? setValueHook = null) { _setValueHook = setValueHook; _value = initialValue; } - protected async Task NotifySubscribersAsync(T? newValue, CancellationToken cancellationToken = default) + protected async Task NotifySubscribersAsync(T newValue, CancellationToken cancellationToken = default) { - List> subscribers; + List> subscribers; lock (_subscriberLock) { subscribers = _subscribers.ToList(); @@ -54,7 +49,7 @@ public abstract class DeclarativePropertyBase : IDeclarativeProperty protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - public IDisposable Subscribe(Func onChange) + public IDisposable Subscribe(Func onChange) { lock (_subscriberLock) { @@ -70,7 +65,7 @@ public abstract class DeclarativePropertyBase : IDeclarativeProperty { lock (_subscriberLock) { - _subscribers.Remove(onChange!); + _subscribers.Remove(onChange); } } @@ -99,14 +94,14 @@ public abstract class DeclarativePropertyBase : IDeclarativeProperty return Task.CompletedTask; }); - protected async Task SetNewValueAsync(T? newValue, CancellationToken cancellationToken = default) + protected async Task SetNewValueAsync(T newValue, CancellationToken cancellationToken = default) { SetNewValueSync(newValue, cancellationToken); if (cancellationToken.IsCancellationRequested) return; await NotifySubscribersAsync(newValue, cancellationToken); } - protected void SetNewValueSync(T? newValue, CancellationToken cancellationToken = default) + protected void SetNewValueSync(T newValue, CancellationToken cancellationToken = default) { if (!(Value?.Equals(newValue) ?? false)) { diff --git a/src/Library/DeclarativeProperty/DeclarativePropertyExtensions.cs b/src/Library/DeclarativeProperty/DeclarativePropertyExtensions.cs index 7c91307..27f3d32 100644 --- a/src/Library/DeclarativeProperty/DeclarativePropertyExtensions.cs +++ b/src/Library/DeclarativeProperty/DeclarativePropertyExtensions.cs @@ -21,16 +21,16 @@ public static class DeclarativePropertyExtensions public static IDeclarativeProperty DistinctUntilChanged(this IDeclarativeProperty from) => new DistinctUntilChangedProperty(from); - public static IDeclarativeProperty Map(this IDeclarativeProperty from, Func> mapper) + public static IDeclarativeProperty Map(this IDeclarativeProperty from, Func> mapper) => Map(from, async (v, _) => await mapper(v)); - public static IDeclarativeProperty Map(this IDeclarativeProperty from, Func> mapper) - => new MapProperty(mapper, from); + public static IDeclarativeProperty Map(this IDeclarativeProperty from, Func> mapper) + => new MapProperty(mapper, from); - public static IDeclarativeProperty Map(this IDeclarativeProperty from, Func mapper) - => new MapProperty((next, _) => Task.FromResult(mapper(next)), from); + public static IDeclarativeProperty Map(this IDeclarativeProperty from, Func mapper) + => new MapProperty((next, _) => Task.FromResult(mapper(next)), from); - public static async Task> MapAsync(this IDeclarativeProperty from, Func> mapper) + public static async Task> MapAsync(this IDeclarativeProperty from, Func> mapper) => await MapProperty.CreateAsync(mapper, from); public static async Task> MapAsync(this IDeclarativeProperty from, Func mapper) @@ -43,39 +43,39 @@ public static class DeclarativePropertyExtensions return Task.CompletedTask; }); - public static IDisposable Subscribe(this IDeclarativeProperty property, Action onChange) + public static IDisposable Subscribe(this IDeclarativeProperty property, Action onChange) => property.Subscribe((value, _) => { onChange(value); return Task.CompletedTask; }); - public static IDeclarativeProperty Extract( - this IDeclarativeProperty> from, - Func?, T?> extractor + public static IDeclarativeProperty Extract( + this IDeclarativeProperty?> from, + Func?, TResult> extractor ) - => new ExtractorProperty(from, extractor); + => new ExtractorProperty(from, extractor); - public static IDeclarativeProperty Extract( - this IDeclarativeProperty> from, - Func?, T?> extractor + public static IDeclarativeProperty Extract( + this IDeclarativeProperty?> from, + Func?, TResult> extractor ) - => new ExtractorProperty(from, extractor); + => new ExtractorProperty(from, extractor); - public static IDeclarativeProperty Extract( - this IDeclarativeProperty> from, - Func?, T?> extractor + public static IDeclarativeProperty Extract( + this IDeclarativeProperty?> from, + Func?, TResult> extractor ) - => new ExtractorProperty(from, extractor); + => new ExtractorProperty(from, extractor); public static IDeclarativeProperty Watch( - this IDeclarativeProperty collection) - where TCollection : IList, INotifyCollectionChanged + this IDeclarativeProperty collection) + where TCollection : IList?, INotifyCollectionChanged? => new CollectionRepeaterProperty(collection); public static IDeclarativeProperty Watch( this TCollection collection) - where TCollection : IList, INotifyCollectionChanged + where TCollection : IList?, INotifyCollectionChanged? => new CollectionRepeaterProperty(collection); public static IDeclarativeProperty> Watch( @@ -90,16 +90,16 @@ public static class DeclarativePropertyExtensions public static IDeclarativeProperty CombineLatest( this IDeclarativeProperty prop1, IDeclarativeProperty prop2, - Func> func, + Func> func, Action? setValueHook = null) - => new CombineLatestProperty(prop1, prop2, func!, setValueHook); + => new CombineLatestProperty(prop1, prop2, func, setValueHook); - public static IDeclarativeProperty Switch(this IDeclarativeProperty?> from) - => new SwitchProperty(from); + public static IDeclarativeProperty Switch(this IDeclarativeProperty?> from) + => new SwitchProperty(from); public static IDeclarativeProperty CombineAll( this IEnumerable> sources, - Func, Task> combiner, + Func, Task> combiner, Action? setValueHook = null) => new CombineAllProperty(sources, combiner, setValueHook); } \ No newline at end of file diff --git a/src/Library/DeclarativeProperty/DistinctUntilChangedProperty.cs b/src/Library/DeclarativeProperty/DistinctUntilChangedProperty.cs index 21d11a6..5d556b7 100644 --- a/src/Library/DeclarativeProperty/DistinctUntilChangedProperty.cs +++ b/src/Library/DeclarativeProperty/DistinctUntilChangedProperty.cs @@ -11,7 +11,7 @@ public sealed class DistinctUntilChangedProperty : DeclarativePropertyBase AddDisposable(from.Subscribe(Handle)); } - async Task Handle(T? next, CancellationToken cancellationToken = default) + async Task Handle(T next, CancellationToken cancellationToken = default) { if (_comparer is { } comparer) { @@ -20,8 +20,10 @@ public sealed class DistinctUntilChangedProperty : DeclarativePropertyBase return; } } - else if ((next is null && Value is null && !_firstFire) - || (Value?.Equals(next) ?? false)) + else if ( + (next is null && Value is null && !_firstFire) + || (Value?.Equals(next) ?? false) + ) { return; } diff --git a/src/Library/DeclarativeProperty/ExtractorProperty.cs b/src/Library/DeclarativeProperty/ExtractorProperty.cs index 7129faf..2cb61b9 100644 --- a/src/Library/DeclarativeProperty/ExtractorProperty.cs +++ b/src/Library/DeclarativeProperty/ExtractorProperty.cs @@ -4,7 +4,7 @@ using ObservableComputations; namespace DeclarativeProperty; -public sealed class ExtractorProperty : DeclarativePropertyBase +public sealed class ExtractorProperty : DeclarativePropertyBase { private interface ICollectionWrapper : IDisposable { @@ -108,12 +108,12 @@ public sealed class ExtractorProperty : DeclarativePropertyBase ) => new CollectionComputingWrapper(collection, handler); } - private readonly Func?, T?> _extractor; + private readonly Func?, TResult> _extractor; private ICollectionWrapper? _collectionWrapper; public ExtractorProperty( - IDeclarativeProperty> from, - Func?, T?> extractor) : base(extractor(from.Value)) + IDeclarativeProperty?> from, + Func?, TResult> extractor) : base(extractor(from.Value)) { _extractor = extractor; _collectionWrapper = from.Value is null @@ -124,8 +124,8 @@ public sealed class ExtractorProperty : DeclarativePropertyBase } public ExtractorProperty( - IDeclarativeProperty> from, - Func?, T?> extractor) : base(extractor(from.Value)) + IDeclarativeProperty?> from, + Func?, TResult> extractor) : base(extractor(from.Value)) { _extractor = extractor; _collectionWrapper = from.Value is null @@ -136,8 +136,8 @@ public sealed class ExtractorProperty : DeclarativePropertyBase } public ExtractorProperty( - IDeclarativeProperty> from, - Func?, T?> extractor) : base(extractor(from.Value)) + IDeclarativeProperty?> from, + Func?, TResult> extractor) : base(extractor(from.Value)) { _extractor = extractor; _collectionWrapper = from.Value is null @@ -165,9 +165,7 @@ public sealed class ExtractorProperty : DeclarativePropertyBase private async Task Fire(IList? items, CancellationToken cancellationToken = default) { - var newValue = items is null - ? default - : _extractor(items); + var newValue = _extractor(items); await SetNewValueAsync(newValue, cancellationToken); } diff --git a/src/Library/DeclarativeProperty/FilterProperty.cs b/src/Library/DeclarativeProperty/FilterProperty.cs index 028c0c0..3f0dd1e 100644 --- a/src/Library/DeclarativeProperty/FilterProperty.cs +++ b/src/Library/DeclarativeProperty/FilterProperty.cs @@ -7,24 +7,33 @@ public sealed class FilterProperty : DeclarativePropertyBase public FilterProperty( Func> filter, IDeclarativeProperty from, - Action? setValueHook = null) : base(setValueHook) + Action? setValueHook = null) : base(default!, setValueHook) { _filter = filter; + + var initialValueTask = Task.Run(async () => await _filter(from.Value)); + initialValueTask.Wait(); + if (initialValueTask.Result) + { + SetNewValueSync(from.Value); + } + // Unfortunately we can't set a default value if the parent current value can not passed by the filter. + AddDisposable(from.Subscribe(SetValue)); } - private async Task SetValue(T? next, CancellationToken cancellationToken = default) + private async Task SetValue(T next, CancellationToken cancellationToken = default) { if (await _filter(next)) { await SetNewValueAsync(next, cancellationToken); } } - + public static async Task> CreateAsync( - Func> filter, + Func> filter, IDeclarativeProperty from, - Action? setValueHook = null) + Action? setValueHook = null) { var prop = new FilterProperty(filter, from, setValueHook); await prop.SetValue(from.Value); diff --git a/src/Library/DeclarativeProperty/IDeclarativeProperty.cs b/src/Library/DeclarativeProperty/IDeclarativeProperty.cs index 0dbcee9..afeccfb 100644 --- a/src/Library/DeclarativeProperty/IDeclarativeProperty.cs +++ b/src/Library/DeclarativeProperty/IDeclarativeProperty.cs @@ -4,9 +4,9 @@ namespace DeclarativeProperty; public interface IDeclarativeProperty : INotifyPropertyChanged, IDisposable, IObservable { - T? Value { get; set; } - IDisposable Subscribe(Func onChange); - void Unsubscribe(Func onChange); + T Value { get; set; } + IDisposable Subscribe(Func onChange); + void Unsubscribe(Func onChange); IDeclarativeProperty RegisterTrigger( Func, T, IDisposable?> triggerSubscribe, diff --git a/src/Library/DeclarativeProperty/MapProperty.cs b/src/Library/DeclarativeProperty/MapProperty.cs index 2526beb..464b020 100644 --- a/src/Library/DeclarativeProperty/MapProperty.cs +++ b/src/Library/DeclarativeProperty/MapProperty.cs @@ -6,14 +6,19 @@ public sealed class MapProperty : DeclarativePropertyBase public MapProperty( Func> mapper, - IDeclarativeProperty from, - Action? setValueHook = null) : base(setValueHook) + IDeclarativeProperty from, + Action? setValueHook = null) : base(default!, setValueHook) { _mapper = mapper; + + var initialValueTask = Task.Run(async () => await _mapper(from.Value, CancellationToken.None)); + initialValueTask.Wait(); + SetNewValueSync(initialValueTask.Result); + AddDisposable(from.Subscribe(SetValue)); } - private async Task SetValue(TFrom? next, CancellationToken cancellationToken = default) + private async Task SetValue(TFrom next, CancellationToken cancellationToken = default) { var newValue = await _mapper(next!, cancellationToken); await SetNewValueAsync(newValue, cancellationToken); @@ -21,8 +26,8 @@ public sealed class MapProperty : DeclarativePropertyBase public static async Task> CreateAsync( Func> mapper, - IDeclarativeProperty from, - Action? setValueHook = null) + IDeclarativeProperty from, + Action? setValueHook = null) { var prop = new MapProperty(mapper, from, setValueHook); await prop.SetValue(from.Value); diff --git a/src/Library/DeclarativeProperty/MergeProperty.cs b/src/Library/DeclarativeProperty/MergeProperty.cs index b317306..49a990d 100644 --- a/src/Library/DeclarativeProperty/MergeProperty.cs +++ b/src/Library/DeclarativeProperty/MergeProperty.cs @@ -2,7 +2,7 @@ public class MergeProperty : DeclarativePropertyBase { - public MergeProperty(params IDeclarativeProperty[] props) + public MergeProperty(params IDeclarativeProperty[] props) : base(default!) { ArgumentNullException.ThrowIfNull(props); @@ -12,6 +12,6 @@ public class MergeProperty : DeclarativePropertyBase } } - private async Task UpdateAsync(T? newValue, CancellationToken token) + private async Task UpdateAsync(T newValue, CancellationToken token) => await SetNewValueAsync(newValue, token); } \ No newline at end of file diff --git a/src/Library/DeclarativeProperty/SwitchProperty.cs b/src/Library/DeclarativeProperty/SwitchProperty.cs index da861d8..bcf8599 100644 --- a/src/Library/DeclarativeProperty/SwitchProperty.cs +++ b/src/Library/DeclarativeProperty/SwitchProperty.cs @@ -4,7 +4,7 @@ public sealed class SwitchProperty : DeclarativePropertyBase { private IDisposable? _innerSubscription; - public SwitchProperty(IDeclarativeProperty?> from) : base(from.Value is null ? default : from.Value.Value) + public SwitchProperty(IDeclarativeProperty?> from) : base(from.Value is null ? default! : from.Value.Value) { AddDisposable(from.Subscribe(HandleStreamChange)); _innerSubscription = from.Value?.Subscribe(HandleInnerValueChange); @@ -15,10 +15,9 @@ public sealed class SwitchProperty : DeclarativePropertyBase _innerSubscription?.Dispose(); _innerSubscription = next?.Subscribe(HandleInnerValueChange); - - await SetNewValueAsync(next is null ? default : next.Value, token); + await SetNewValueAsync(next is null ? default! : next.Value, token); } - private async Task HandleInnerValueChange(TItem? next, CancellationToken token) + private async Task HandleInnerValueChange(TItem next, CancellationToken token) => await SetNewValueAsync(next, token); } \ No newline at end of file diff --git a/src/Library/DeclarativeProperty/ThrottleProperty.cs b/src/Library/DeclarativeProperty/ThrottleProperty.cs index 9fdea22..9e935a1 100644 --- a/src/Library/DeclarativeProperty/ThrottleProperty.cs +++ b/src/Library/DeclarativeProperty/ThrottleProperty.cs @@ -9,13 +9,13 @@ public class ThrottleProperty : DeclarativePropertyBase public ThrottleProperty( IDeclarativeProperty from, Func interval, - Action? setValueHook = null) : base(from.Value, setValueHook) + Action? setValueHook = null) : base(from.Value, setValueHook) { _interval = interval; AddDisposable(from.Subscribe(SetValue)); } - private async Task SetValue(T? next, CancellationToken cancellationToken = default) + private async Task SetValue(T next, CancellationToken cancellationToken = default) { lock (_lock) { diff --git a/src/Library/DeclarativeProperty/Unsubscriber.cs b/src/Library/DeclarativeProperty/Unsubscriber.cs index b0fde53..7958704 100644 --- a/src/Library/DeclarativeProperty/Unsubscriber.cs +++ b/src/Library/DeclarativeProperty/Unsubscriber.cs @@ -3,9 +3,9 @@ internal sealed class Unsubscriber : IDisposable { private readonly IDeclarativeProperty _owner; - private readonly Func _onChange; + private readonly Func _onChange; - public Unsubscriber(IDeclarativeProperty owner, Func onChange) + public Unsubscriber(IDeclarativeProperty owner, Func onChange) { _owner = owner; _onChange = onChange;