2019-10-12 03:07:57 +00:00
|
|
|
|
using System;
|
2019-10-12 18:18:21 +00:00
|
|
|
|
using System.Linq;
|
2019-10-12 03:07:57 +00:00
|
|
|
|
using System.Linq.Expressions;
|
2019-10-12 21:04:14 +00:00
|
|
|
|
using System.Reactive;
|
2020-01-17 04:48:54 +00:00
|
|
|
|
using System.Reactive.Disposables;
|
2019-10-12 03:07:57 +00:00
|
|
|
|
using System.Reactive.Linq;
|
2020-05-25 17:34:25 +00:00
|
|
|
|
using System.Reactive.Subjects;
|
|
|
|
|
using System.Threading.Tasks;
|
2019-10-12 18:18:21 +00:00
|
|
|
|
using DynamicData;
|
|
|
|
|
using DynamicData.Kernel;
|
2019-10-12 03:07:57 +00:00
|
|
|
|
using ReactiveUI;
|
2020-01-17 04:48:54 +00:00
|
|
|
|
using Wabbajack.Lib;
|
2019-10-12 03:07:57 +00:00
|
|
|
|
|
|
|
|
|
namespace Wabbajack
|
|
|
|
|
{
|
|
|
|
|
public static class ReactiveUIExt
|
|
|
|
|
{
|
2019-10-13 06:38:51 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Convenience function to not have to specify the selector function in the default ReactiveUI WhenAny() call.
|
|
|
|
|
/// Subscribes to changes in a property on a given object.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="TSender">Type of object to watch</typeparam>
|
|
|
|
|
/// <typeparam name="TRet">The type of property watched</typeparam>
|
|
|
|
|
/// <param name="This">Object to watch</param>
|
|
|
|
|
/// <param name="property1">Expression path to the property to subscribe to</param>
|
|
|
|
|
/// <returns></returns>
|
2019-10-12 03:07:57 +00:00
|
|
|
|
public static IObservable<TRet> WhenAny<TSender, TRet>(
|
|
|
|
|
this TSender This,
|
|
|
|
|
Expression<Func<TSender, TRet>> property1)
|
|
|
|
|
where TSender : class
|
|
|
|
|
{
|
|
|
|
|
return This.WhenAny(property1, selector: x => x.GetValue());
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-13 06:38:51 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Convenience wrapper to observe following calls on the GUI thread.
|
|
|
|
|
/// </summary>
|
2019-10-12 18:42:47 +00:00
|
|
|
|
public static IObservable<T> ObserveOnGuiThread<T>(this IObservable<T> source)
|
|
|
|
|
{
|
|
|
|
|
return source.ObserveOn(RxApp.MainThreadScheduler);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-25 17:34:25 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Like IObservable.Select but supports async map functions
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="source"></param>
|
|
|
|
|
/// <param name="f"></param>
|
|
|
|
|
/// <typeparam name="T"></typeparam>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static IObservable<TOut> SelectAsync<TIn, TOut>(this IObservable<TIn> source, Func<TIn, Task<TOut>> f)
|
|
|
|
|
{
|
|
|
|
|
return source.Select(itm => Observable.FromAsync(async () => await f(itm))).Merge(10);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-13 08:03:41 +00:00
|
|
|
|
public static IObservable<Unit> StartingExecution(this IReactiveCommand cmd)
|
|
|
|
|
{
|
|
|
|
|
return cmd.IsExecuting
|
|
|
|
|
.DistinctUntilChanged()
|
|
|
|
|
.Where(x => x)
|
|
|
|
|
.Unit();
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-18 03:10:38 +00:00
|
|
|
|
public static IObservable<Unit> EndingExecution(this IReactiveCommand cmd)
|
|
|
|
|
{
|
|
|
|
|
return cmd.IsExecuting
|
|
|
|
|
.DistinctUntilChanged()
|
|
|
|
|
.Pairwise()
|
|
|
|
|
.Where(x => x.Previous && !x.Current)
|
|
|
|
|
.Unit();
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-12 18:18:21 +00:00
|
|
|
|
/// These snippets were provided by RolandPheasant (author of DynamicData)
|
|
|
|
|
/// They'll be going into the official library at some point, but are here for now.
|
2019-10-13 06:38:51 +00:00
|
|
|
|
#region Dynamic Data EnsureUniqueChanges
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Removes outdated key events from a changeset, only leaving the last relevent change for each key.
|
|
|
|
|
/// </summary>
|
2019-10-12 18:18:21 +00:00
|
|
|
|
public static IObservable<IChangeSet<TObject, TKey>> EnsureUniqueChanges<TObject, TKey>(this IObservable<IChangeSet<TObject, TKey>> source)
|
2021-01-01 18:21:07 +00:00
|
|
|
|
where TKey : notnull
|
2019-10-12 18:18:21 +00:00
|
|
|
|
{
|
|
|
|
|
return source.Select(EnsureUniqueChanges);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-13 06:38:51 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Removes outdated key events from a changeset, only leaving the last relevent change for each key.
|
|
|
|
|
/// </summary>
|
2019-10-12 18:18:21 +00:00
|
|
|
|
public static IChangeSet<TObject, TKey> EnsureUniqueChanges<TObject, TKey>(this IChangeSet<TObject, TKey> input)
|
2021-01-01 18:21:07 +00:00
|
|
|
|
where TKey : notnull
|
|
|
|
|
|
2019-10-12 18:18:21 +00:00
|
|
|
|
{
|
|
|
|
|
var changes = input
|
|
|
|
|
.GroupBy(kvp => kvp.Key)
|
|
|
|
|
.Select(g => g.Aggregate(Optional<Change<TObject, TKey>>.None, Reduce))
|
|
|
|
|
.Where(x => x.HasValue)
|
|
|
|
|
.Select(x => x.Value);
|
|
|
|
|
|
|
|
|
|
return new ChangeSet<TObject, TKey>(changes);
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-17 04:48:54 +00:00
|
|
|
|
public static ObservableAsPropertyHelper<TRet> ToGuiProperty<TRet>(
|
|
|
|
|
this IObservable<TRet> source,
|
|
|
|
|
ViewModel vm,
|
|
|
|
|
string property,
|
2021-03-12 13:49:34 +00:00
|
|
|
|
TRet? initialValue = default,
|
2020-01-17 04:48:54 +00:00
|
|
|
|
bool deferSubscription = false)
|
|
|
|
|
{
|
|
|
|
|
return source
|
|
|
|
|
.ToProperty(vm, property, initialValue, deferSubscription, RxApp.MainThreadScheduler)
|
2020-11-18 11:37:31 +00:00
|
|
|
|
.DisposeWith(vm.CompositeDisposable)!;
|
2020-01-17 04:48:54 +00:00
|
|
|
|
}
|
2021-01-01 18:21:07 +00:00
|
|
|
|
/*
|
2020-01-17 04:48:54 +00:00
|
|
|
|
public static void ToGuiProperty<TRet>(
|
|
|
|
|
this IObservable<TRet> source,
|
|
|
|
|
ViewModel vm,
|
|
|
|
|
string property,
|
|
|
|
|
out ObservableAsPropertyHelper<TRet> result,
|
|
|
|
|
TRet initialValue = default,
|
|
|
|
|
bool deferSubscription = false)
|
|
|
|
|
{
|
2021-01-01 18:21:07 +00:00
|
|
|
|
|
2020-11-18 11:37:31 +00:00
|
|
|
|
source.ToProperty(vm, property, out result!, initialValue, deferSubscription, RxApp.MainThreadScheduler)
|
2020-01-17 04:48:54 +00:00
|
|
|
|
.DisposeWith(vm.CompositeDisposable);
|
2021-01-01 18:21:07 +00:00
|
|
|
|
}*/
|
2019-10-12 18:18:21 +00:00
|
|
|
|
|
|
|
|
|
internal static Optional<Change<TObject, TKey>> Reduce<TObject, TKey>(Optional<Change<TObject, TKey>> previous, Change<TObject, TKey> next)
|
2021-01-01 18:21:07 +00:00
|
|
|
|
where TKey : notnull
|
|
|
|
|
|
2019-10-12 18:18:21 +00:00
|
|
|
|
{
|
|
|
|
|
if (!previous.HasValue)
|
|
|
|
|
{
|
|
|
|
|
return next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var previousValue = previous.Value;
|
|
|
|
|
|
|
|
|
|
switch (previousValue.Reason)
|
|
|
|
|
{
|
|
|
|
|
case ChangeReason.Add when next.Reason == ChangeReason.Remove:
|
|
|
|
|
return Optional<Change<TObject, TKey>>.None;
|
|
|
|
|
|
|
|
|
|
case ChangeReason.Remove when next.Reason == ChangeReason.Add:
|
|
|
|
|
return new Change<TObject, TKey>(ChangeReason.Update, next.Key, next.Current, previousValue.Current, next.CurrentIndex, previousValue.CurrentIndex);
|
|
|
|
|
|
|
|
|
|
case ChangeReason.Add when next.Reason == ChangeReason.Update:
|
|
|
|
|
return new Change<TObject, TKey>(ChangeReason.Add, next.Key, next.Current, next.CurrentIndex);
|
|
|
|
|
|
|
|
|
|
case ChangeReason.Update when next.Reason == ChangeReason.Update:
|
|
|
|
|
return new Change<TObject, TKey>(ChangeReason.Update, previousValue.Key, next.Current, previousValue.Previous, next.CurrentIndex, previousValue.PreviousIndex);
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
2019-10-12 03:07:57 +00:00
|
|
|
|
}
|
|
|
|
|
}
|