Last active
April 28, 2023 23:20
-
-
Save ruxo/6c21b773ce4cdb3cfc82deeafe632bd1 to your computer and use it in GitHub Desktop.
(Broken) AsyncVal for UI async execution for FSharp.Data.Adaptive
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [<AutoOpen>] | |
| module Tirax.KMS.AdaptiveExtension | |
| open System | |
| open System.Runtime.CompilerServices | |
| open FSharp.Data.Adaptive | |
| [<Struct; IsReadOnly>] | |
| type AsyncResult<'T> = | |
| | Loading | |
| | LoadError of error:exn | |
| | Data of 'T | |
| [<Struct; IsReadOnly>] | |
| type CachedValue<'K, 'T when 'K :equality> = { | |
| input :'K | |
| output :AsyncResult<'T> | |
| last_updated :DateTime | |
| } | |
| with | |
| member my.isSame time_limit v = my.input = v && DateTime.Now - my.last_updated < time_limit | |
| let private setCache struct (k,v) = { input=k; output=v; last_updated = DateTime.Now } | |
| [<Sealed>] | |
| type AsyncVal<'TI, 'TO when 'TI :equality>(async_func :'TI -> Async<'TO>, input :aval<'TI>) = | |
| inherit AdaptiveObject() | |
| let mutable current = { input=Unchecked.defaultof<'TI>; output = Loading; last_updated = DateTime.MinValue } | |
| interface IAdaptiveValue<AsyncResult<'TO>> with | |
| member my.GetValue t = my.GetValue t | |
| member me.Accept(visitor) = visitor.Visit me | |
| member my.ContentType = typeof<AsyncResult<'TO>> | |
| member my.GetValueUntyped(token) = my.GetValue(token) |> box | |
| member my.GetValue token = my.EvaluateIfNeeded token current.output my.compute | |
| member private my.compute(token) = | |
| let k = input.GetValue token | |
| if not <| current.isSame (TimeSpan.FromSeconds 1) k then | |
| current <- setCache struct (k, Loading) | |
| my.scheduleUpdate k | |
| current.output | |
| member private my.scheduleUpdate input_value = | |
| Async.StartWithContinuations | |
| (async_func input_value, | |
| (fun v -> my.publish(input_value, Data v)), | |
| (fun e -> my.publish(input_value, LoadError e)), | |
| (fun _ -> my.publish(input_value, current.output))) | |
| member private my.publish(k,v) = | |
| transact(fun () -> current <- setCache(k,v) | |
| my.MarkOutdated()) | |
| [<Extension>] | |
| type AValExtension = | |
| [<Extension>] | |
| static member inline mapAsync(my :aval<'T>, async_func) = | |
| AsyncVal(async_func, my) :> aval<AsyncResult<'R>> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [<AutoOpen>] | |
| module Fun.Blazor.DslObservable | |
| open System | |
| open System.Reactive.Subjects | |
| open System.Runtime.CompilerServices | |
| open System.Runtime.InteropServices | |
| open FSharp.Control.Reactive | |
| open RZ.FSharp.Extension | |
| [<Struct; IsReadOnly>] | |
| type UiAsyncResult<'T> = | |
| | Loading | |
| | LoadError of error:exn | |
| | Data of 'T | |
| let uiAsync f x = async { | |
| try | |
| let! data = f x | |
| return (Data data) | |
| with | |
| | e -> return (LoadError e) | |
| } | |
| module Helpers = | |
| let empty_attrs = Internal.emptyAttr() | |
| let empty_node = Internal.emptyNode() | |
| let render (x :NodeRenderFragment) = x | |
| [<Struct; IsByRefLike>] | |
| type ObservableModel<'T>(observable :IObservable<'T>, [<Optional>] initial_view :NodeRenderFragment voption) = | |
| member _.Observable = observable | |
| member _.InitialView = initial_view | |
| open Operators | |
| type IObservable<'T> with | |
| member my.bindUiAsync(f) = | |
| my.bind(fun x -> Observable.single(Loading).concat (Observable.ofAsync(uiAsync f x))) | |
| member source.render(render :'T -> NodeRenderFragment, default_value :'T, k :obj voption) = | |
| ComponentWithChildBuilder<ReactiveComponent<'T>>() { | |
| "DefaultValue" => default_value | |
| "Store" => source | |
| "RenderFn" => render | |
| match k with | |
| | ValueSome k -> html.key k | |
| | ValueNone -> Internal.emptyAttr() | |
| } | |
| [<Struct; IsReadOnly>] | |
| type ObserviewBuilder([<Optional; DefaultParameterValue null>] k :obj) = | |
| member inline _.Zero() = Seq.empty | |
| member inline _.Yield(x :NodeRenderFragment) = Seq.singleton x | |
| member inline _.Delay([<InlineIfLambda>] fn :unit -> NodeRenderFragment seq) = fn() | |
| member inline _.Combine(x :NodeRenderFragment seq, y :NodeRenderFragment seq) = y.append(x) | |
| member _.Bind(source :IObservable<'T>, f :'T -> NodeRenderFragment seq) = | |
| source.render(f >> html.mergeNodes, Unchecked.defaultof<'T>, ValueOption.ofObj k) |> Seq.singleton | |
| member _.Bind(x :ObservableModel<'T>, f :'T -> NodeRenderFragment seq) = | |
| let source = x.Observable |> Observable.map ValueSome | |
| let init_view = x.InitialView | |
| let render opt = match opt with | |
| | ValueSome v -> v |> f |> html.mergeNodes | |
| | ValueNone -> init_view.defaultValue(Helpers.empty_node) | |
| source.render(render, ValueNone, ValueOption.ofObj k) |> Seq.singleton | |
| member inline _.Run(x :NodeRenderFragment seq) = | |
| html.mergeNodes(x) | |
| [<Extension>] | |
| type Extensions = | |
| [<Extension>] | |
| static member WithSetter(this :ISubject<'T>) = | |
| this |> Observable.map (fun x -> struct (x, this.OnNext)) | |
| type obserview = ObserviewBuilder |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment