Skip to content

Commit

Permalink
experimental features:
Browse files Browse the repository at this point in the history
- `.=` operator to assign/set the value of an `IWritable`
- `ctx.useDeferred`, `ctx.useDeferredCallback`
  • Loading branch information
JaggerJo committed Oct 22, 2023
1 parent 446845d commit 151b9a9
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/Avalonia.FuncUI/Avalonia.FuncUI.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@
</None>
<Compile Include="Experimantal\Experimental.Animations.fs" />
<Compile Include="Experimantal\Experimental.EnvironmentState.fs" />
<Compile Include="Experimantal\Experimental.Operators.fs" />
<Compile Include="Experimantal\Experimental.Hooks.fs" />
</ItemGroup>

</Project>
109 changes: 109 additions & 0 deletions src/Avalonia.FuncUI/Experimantal/Experimental.Hooks.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
namespace Avalonia.FuncUI.Experimental

open System
open System.Runtime.CompilerServices
open System.Threading
open Avalonia.Controls
open Avalonia.FuncUI
open Avalonia.FuncUI.DSL
open Avalonia.FuncUI.Types
open Avalonia.Media

(*
Ported from https://github.com/Zaid-Ajaj/Feliz/tree/master/Feliz.UseDeferred by Zaid Ajaj
*)

[<RequireQualifiedAccess>]
type Deferred<'T> =
| HasNotStartedYet
| InProgress
| Resolved of 'T
| Failed of exn

[<AutoOpen>]
module ContextExtensions =

type IComponentContext with

member ctx.useDeferred(operation: Async<'T>, dependencies: IAnyReadable seq) =
let deferred = ctx.useState Deferred.HasNotStartedYet

let executeOperation = async {
try
do deferred .= Deferred<'T>.InProgress
let! output = operation
do deferred .= Deferred<'T>.Resolved output
with error ->
do deferred .= (Deferred<'T>.Failed error)
}

ctx.useEffect (
handler = (fun () ->
let ct = new CancellationTokenSource()

Async.Start (executeOperation, ct.Token)

ct :> IDisposable
),
triggers = [
yield EffectTrigger.AfterInit

for dependency in dependencies do
yield EffectTrigger.AfterChange dependency
]

)

deferred

member ctx.useDeferredCallback(operation: 'TIn -> Async<'TOut>, setDeferred: Deferred<'TOut> -> unit) =
let cancellationToken = ctx.useState(new CancellationTokenSource(), renderOnChange = false)
let executeOperation arg = async {
try
do setDeferred(Deferred<'TOut>.InProgress)
let! output = operation arg
do setDeferred(Deferred<'TOut>.Resolved output)
with error ->
do setDeferred(Deferred<'TOut>.Failed error)
}

ctx.useEffect (
handler = (fun () ->
cancellationToken.Current :> IDisposable
),
triggers = [ EffectTrigger.AfterInit ]
)

let start (arg: 'TIn) =
if not cancellationToken.Current.IsCancellationRequested then
Async.Start(executeOperation arg, cancellationToken.Current.Token)

start

[<AbstractClass; Sealed; Extension>]
type DeferredExtensions =

[<Extension>]
static member Map(data: IReadable<Deferred<'t>>, func: 't -> IView) : IView =
match data.Current with
| Deferred.HasNotStartedYet ->
ProgressBar.create [
ProgressBar.isIndeterminate false
]

| Deferred.InProgress ->
ProgressBar.create [
ProgressBar.isIndeterminate true
]

| Deferred.Resolved data ->
func data

| Deferred.Failed ex ->
TextBlock.create [
TextBlock.text $"Error: %A{ex}"
TextBlock.foreground Brushes.Red
]
|> generalize


9 changes: 9 additions & 0 deletions src/Avalonia.FuncUI/Experimantal/Experimental.Operators.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Avalonia.FuncUI.Experimental

open Avalonia.FuncUI

[<AutoOpen>]
module CustomOperators =

let (.=) (state: IWritable<'t>) (value: 't) =
state.Set value

0 comments on commit 151b9a9

Please sign in to comment.