From 78e8f28e7207bb3cbd3337805c4820995cb540e1 Mon Sep 17 00:00:00 2001 From: Josua Jaeger Date: Thu, 5 Sep 2024 21:38:30 +0200 Subject: [PATCH] fix https://github.com/fsprojects/Avalonia.FuncUI/issues/435 --- .../Avalonia.FuncUI.UnitTests.fsproj | 1 + src/Avalonia.FuncUI.UnitTests/LibTests.fs | 24 +++++++++++++++-- src/Avalonia.FuncUI/DSL/AttrBuilder.fs | 4 +++ src/Avalonia.FuncUI/Library.fs | 27 +++++++++++++++++-- 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.FuncUI.UnitTests/Avalonia.FuncUI.UnitTests.fsproj b/src/Avalonia.FuncUI.UnitTests/Avalonia.FuncUI.UnitTests.fsproj index 2b830e9f..2e550c8e 100644 --- a/src/Avalonia.FuncUI.UnitTests/Avalonia.FuncUI.UnitTests.fsproj +++ b/src/Avalonia.FuncUI.UnitTests/Avalonia.FuncUI.UnitTests.fsproj @@ -23,6 +23,7 @@ + all diff --git a/src/Avalonia.FuncUI.UnitTests/LibTests.fs b/src/Avalonia.FuncUI.UnitTests/LibTests.fs index fae51730..f9f7b9e1 100644 --- a/src/Avalonia.FuncUI.UnitTests/LibTests.fs +++ b/src/Avalonia.FuncUI.UnitTests/LibTests.fs @@ -1,5 +1,6 @@ namespace Avalonia.FuncUI.UnitTests +open System.Reactive.Subjects open Avalonia.FuncUI.Library open Xunit @@ -7,7 +8,7 @@ module Library = [] let ``function equality`` () = - + let view () = let add = fun (count: int) -> count + 1 let sub = fun (count: int) -> count - 1 @@ -19,4 +20,23 @@ module Library = Assert.Equal(add'.GetType(), add''.GetType()) Assert.Equal(sub'.GetType(), sub''.GetType()) Assert.NotEqual(add'.GetType(), sub'.GetType()) - Assert.NotEqual(add''.GetType(), sub''.GetType()) \ No newline at end of file + Assert.NotEqual(add''.GetType(), sub''.GetType()) + + + [] + let ``IObservable.SkipFirst`` () = + let observable = new Subject() + let seenItems = ResizeArray() + + let _ = + observable + .SkipFirst() + .Subscribe(fun n -> seenItems.Add(n)) + + observable.OnNext(1) + observable.OnNext(2) + observable.OnNext(3) + + Assert.Equal(2, seenItems.Count) + Assert.Equal(2, seenItems.[0]) + Assert.Equal(3, seenItems.[1]) \ No newline at end of file diff --git a/src/Avalonia.FuncUI/DSL/AttrBuilder.fs b/src/Avalonia.FuncUI/DSL/AttrBuilder.fs index 434f8f94..fff01dd2 100644 --- a/src/Avalonia.FuncUI/DSL/AttrBuilder.fs +++ b/src/Avalonia.FuncUI/DSL/AttrBuilder.fs @@ -235,6 +235,8 @@ type AttrBuilder<'view>() = let cts = new CancellationTokenSource() control .GetObservable(property) + // GetObservable immediately emits the current value. We're not interested in that, so we skip the first value. + .SkipFirst() .Subscribe(func, cts.Token) cts @@ -261,6 +263,8 @@ type AttrBuilder<'view>() = let cts = new CancellationTokenSource() control .GetObservable(property) + // GetObservable immediately emits the current value. We're not interested in that, so we skip the first value. + .SkipFirst() .Subscribe(func, cts.Token) cts diff --git a/src/Avalonia.FuncUI/Library.fs b/src/Avalonia.FuncUI/Library.fs index a5d75a1e..13ab3e50 100644 --- a/src/Avalonia.FuncUI/Library.fs +++ b/src/Avalonia.FuncUI/Library.fs @@ -37,6 +37,29 @@ module internal Extensions = let disposable = Observable.subscribe callback this token.Register(fun () -> disposable.Dispose()) |> ignore + member this.SkipFirst() = + { new IObservable<'a> with + member __.Subscribe(observer: IObserver<'a>) = + let skipFirstObserver = + let mutable isFirst = true + + { new IObserver<'a> with + member __.OnNext(value) = + if isFirst then + isFirst <- false + else + observer.OnNext(value) + + member __.OnError(error) = + observer.OnError(error) + + member __.OnCompleted() = + observer.OnCompleted() + } + + this.Subscribe(skipFirstObserver) + } + type Interactive with member this.GetObservable<'args when 'args :> RoutedEventArgs>(routedEvent: RoutedEvent<'args>) : IObservable<'args> = let sub = Func, IDisposable>(fun observer -> @@ -44,10 +67,10 @@ module internal Extensions = let handler = EventHandler<'args>(fun _ e -> observer.OnNext e ) - + // subscribe to event changes so they can be pushed to subscribers this.AddDisposableHandler(routedEvent, handler, routedEvent.RoutingStrategies) ) - + { new IObservable<'args> with member this.Subscribe(observer: IObserver<'args>) = sub.Invoke(observer) } \ No newline at end of file