Skip to content

Commit

Permalink
Added OnItemAdded and OnItemRemoved for collections
Browse files Browse the repository at this point in the history
  • Loading branch information
YohDeadfall committed Jun 29, 2024
1 parent 2e77b2d commit da4575a
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 0 deletions.
17 changes: 17 additions & 0 deletions src/Kinetic/Linq/ObservableView.OnItemAdded.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using Kinetic.Linq.StateMachines;

namespace Kinetic.Linq;

public static partial class ObservableView
{
public static ObserverBuilder<ListChange<TSource>> OnItemAdded<TSource>(this ObserverBuilder<ListChange<TSource>> source, Action<TSource> action) =>
source.Do(change =>
{
if (change.Action is ListChangeAction.Insert or ListChangeAction.Replace)
action(change.NewItem);
});

public static ObserverBuilder<ListChange<TSource>> OnItemAdded<TSource>(this ReadOnlyObservableList<TSource> source, Action<TSource> action) =>
source.Changed.ToBuilder().OnItemAdded(action);
}
110 changes: 110 additions & 0 deletions src/Kinetic/Linq/ObservableView.OnItemRemoved.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using Kinetic.Linq.StateMachines;

namespace Kinetic.Linq;

public static partial class ObservableView
{
public static ObserverBuilder<ListChange<TSource>> OnItemRemoved<TSource>(this ObserverBuilder<ListChange<TSource>> source, Action<TSource> action) =>
source.ContinueWith<OnItemRemovedStateMachineFactory<TSource>, ListChange<TSource>>(new() { Action = action });

public static ObserverBuilder<ListChange<TSource>> OnItemRemoved<TSource>(this ReadOnlyObservableList<TSource> source, Action<TSource> action) =>
source.Changed.ToBuilder().OnItemRemoved(action);

private struct OnItemRemovedStateMachineFactory<TSource> : IStateMachineFactory<ListChange<TSource>, ListChange<TSource>>
{
public required Action<TSource> Action { get; init; }

public void Create<TContinuation>(in TContinuation continuation, ObserverStateMachine<ListChange<TSource>> source)
where TContinuation : struct, IStateMachine<ListChange<TSource>>
{
source.ContinueWith<OnItemRemovedStateMachine<TSource, TContinuation>>(new(continuation, Action));
}
}

private struct OnItemRemovedStateMachine<TSource, TContinuation> : IStateMachine<ListChange<TSource>>
where TContinuation : struct, IStateMachine<ListChange<TSource>>
{
private TContinuation _continuation;
private readonly Action<TSource> _action;
private readonly List<TSource> _items = new();

public OnItemRemovedStateMachine(in TContinuation continuation, Action<TSource> action)
{
_continuation = continuation;
_action = action;
}

public StateMachineBox Box =>
_continuation.Box;

public void Initialize(StateMachineBox box) =>
_continuation.Initialize(box);

public void Dispose() =>
_continuation.Dispose();

public void OnCompleted() =>
_continuation.OnCompleted();

public void OnError(Exception error) =>
_continuation.OnError(error);

public void OnNext(ListChange<TSource> value)
{
_continuation.OnNext(value);

switch (value.Action)
{
case ListChangeAction.RemoveAll:
{
foreach (var item in _items)
_action(item);

_items.Clear();

break;
}
case ListChangeAction.Remove:
{
var index = value.OldIndex;
var item = _items[value.OldIndex];

_action(item);
_items.RemoveAt(index);

break;
}
case ListChangeAction.Insert:
{
_items.Insert(
value.NewIndex,
value.NewItem);

break;
}
case ListChangeAction.Replace:
{
var index = value.OldIndex;
var item = _items[index];

_action(item);
_items[index] = value.NewItem;

break;
}
case ListChangeAction.Move:
{
var index = value.OldIndex;
var item = _items[index];

_items.RemoveAt(index);
_items.Insert(index, item);

break;
}
}
}
}
}
15 changes: 15 additions & 0 deletions src/Kinetic/Linq/ObservableView.OnItemRemovedDispose.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using Kinetic.Linq.StateMachines;

namespace Kinetic.Linq;

public static partial class ObservableView
{
public static ObserverBuilder<ListChange<TSource>> OnItemRemovedDispose<TSource>(this ObserverBuilder<ListChange<TSource>> source)
where TSource : IDisposable =>
source.OnItemRemoved(item => item?.Dispose());

public static ObserverBuilder<ListChange<TSource>> OnItemRemovedDispose<TSource>(this ReadOnlyObservableList<TSource> source)
where TSource : IDisposable =>
source.Changed.ToBuilder().OnItemRemovedDispose();
}
48 changes: 48 additions & 0 deletions test/Kinetic.Tests/ObservableViewTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,54 @@ public void GroupByDynamicWithComparer()
Assert.Empty(groupingsOrdered);
}

[Fact]
public void OnItemAdded()
{
var list = new ObservableList<int>();
var handledBefore = false;
var handledAfter = false;

using var changes = list
.Changed
.Do(_ => handledBefore = true)
.OnItemAdded(item =>
{
Assert.True(handledBefore);
Assert.False(handledAfter);
})
.Do(_ => handledAfter = true)
.Subscribe();

list.Add(0);

Assert.True(handledBefore);
Assert.True(handledAfter);
}

[Fact]
public void OnItemRemoved()
{
var list = new ObservableList<int>();
var handledBefore = false;
var handledAfter = false;

using var changes = list
.Changed
.Do(_ => handledBefore = true)
.OnItemRemoved(item =>
{
Assert.True(handledBefore);
Assert.True(handledAfter);
})
.Do(_ => handledAfter = true)
.Subscribe();

list.Add(0);

Assert.True(handledBefore);
Assert.True(handledAfter);
}

[Fact]
public void OrderByStatic()
{
Expand Down

0 comments on commit da4575a

Please sign in to comment.