Skip to content

Commit

Permalink
feat(mvux): make foreachdata fluent
Browse files Browse the repository at this point in the history
  • Loading branch information
ajpinedam committed Aug 26, 2024
1 parent c65856e commit ddf9b72
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ public async Task When_ForEachAsync_Then_AcceptsNotNullAndStruct()
public async Task When_ForEachDataAsync_Then_AcceptsNotNullAndStruct()
{
_ = default(IState<int>)!.ForEach(async (i, ct) => this.ToString());
default(IState<int?>)!.ForEachDataAsync(async (i, ct) => this.ToString());
default(IState<string>)!.ForEachDataAsync(async (i, ct) => this.ToString());
default(IState<string?>)!.ForEachDataAsync(async (i, ct) => this.ToString());
default(IState<MyStruct>)!.ForEachDataAsync(async (i, ct) => this.ToString());
default(IState<MyStruct?>)!.ForEachDataAsync(async (i, ct) => this.ToString());
default(IState<MyClass>)!.ForEachDataAsync(async (i, ct) => this.ToString());
default(IState<MyClass?>)!.ForEachDataAsync(async (i, ct) => this.ToString());
_ = default(IState<int?>)!.ForEachData(async (i, ct) => this.ToString());
_ = default(IState<string>)!.ForEachData(async (i, ct) => this.ToString());
_ = default(IState<string?>)!.ForEachData(async (i, ct) => this.ToString());
_ = default(IState<MyStruct>)!.ForEachData(async (i, ct) => this.ToString());
_ = default(IState<MyStruct?>)!.ForEachData(async (i, ct) => this.ToString());
_ = default(IState<MyClass>)!.ForEachData(async (i, ct) => this.ToString());
_ = default(IState<MyClass?>)!.ForEachData(async (i, ct) => this.ToString());
}

[TestMethod]
Expand Down
49 changes: 44 additions & 5 deletions src/Uno.Extensions.Reactive/Core/State.Extensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -287,17 +287,56 @@ public static IState<T> ForEach<T>(this IState<T> state, AsyncAction<T?> action,
}


/// <summary>
/// [DEPRECATED] Use ForEachData instead
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
#if DEBUG // To avoid usage in internal reactive code, but without forcing apps to update right away
[Obsolete("Use ForEachData")]
#endif
public static IDisposable ForEachDataAsync<T>(this IState<T> state, AsyncAction<Option<T>> action, [CallerMemberName] string? caller = null, [CallerLineNumber] int line = -1)
=> new StateForEach<T>(state, action, $"ForEachDataAsync defined in {caller} at line {line}.");

/// <summary>
/// Execute an async callback each time the state is being updated.
/// </summary>
/// <typeparam name="T">The type of the state</typeparam>
/// <param name="state">The state to listen.</param>
/// <param name="action">The callback to invoke on each update of the state.</param>
/// <param name="caller"> For debug purposes, the name of this subscription. DO NOT provide anything here, let the compiler fulfill this.</param>
/// <param name="line">For debug purposes, the name of this subscription. DO NOT provide anything here, let the compiler fulfill this.</param>
/// <returns>A <see cref="IDisposable"/> that can be used to remove the callback registration.</returns>
public static IDisposable ForEachDataAsync<T>(this IState<T> state, AsyncAction<Option<T>> action, [CallerMemberName] string? caller = null, [CallerLineNumber] int line = -1)
=> new StateForEach<T>(state, action, $"ForEachDataAsync defined in {caller} at line {line}.");
/// <param name="line">For debug purposes, the name of this subscription. DO NOT provide anything here, let the compiler fulfill this.</param>
/// <returns>An <see cref="IState"/> that can be used to chain other operations.</returns>
public static IState<T> ForEachData<T>(this IState<T> state, AsyncAction<Option<T>> action, [CallerMemberName] string? caller = null, [CallerLineNumber] int line = -1)
{
_ = AttachedProperty.GetOrCreate(
owner: state,
key: action,
state: (caller, line),
factory: static (s, a, d) => new StateForEach<T>(s, a, $"ForEachData defined in {d.caller} at line {d.line}."));

return state;
}

/// <summary>
/// Execute an async callback each time the state is being updated.
/// </summary>
/// <typeparam name="T">The type of the state</typeparam>
/// <param name="state">The state to listen.</param>
/// <param name="action">The callback to invoke on each update of the state.</param>
/// <param name="disposable"> A <see cref="IDisposable"/> that can be used to remove the callback registration.</param>
/// <param name="caller"> For debug purposes, the name of this subscription. DO NOT provide anything here, let the compiler fulfill this.</param>
/// <param name="line">For debug purposes, the name of this subscription. DO NOT provide anything here, let the compiler fulfill this.</param>
/// <returns>An <see cref="IState"/> that can be used to chain other operations.</returns>
public static IState<T> ForEachData<T>(this IState<T> state, AsyncAction<Option<T>> action, out IDisposable disposable, [CallerMemberName] string? caller = null, [CallerLineNumber] int line = -1)
{
disposable = AttachedProperty.GetOrCreate(
owner: state,
key: action,
state: (caller, line),
factory: static (s, a, d) => new StateForEach<T>(s, a, $"ForEachData defined in {d.caller} at line {d.line}."));

return state;
}

/// <summary>
/// [DEPRECATED] Use .ForEachAsync instead
Expand Down

0 comments on commit ddf9b72

Please sign in to comment.