diff --git a/Content/default/paket.dependencies b/Content/default/paket.dependencies index 302c3da0..8c2aecee 100644 --- a/Content/default/paket.dependencies +++ b/Content/default/paket.dependencies @@ -2,19 +2,9 @@ source https://api.nuget.org/v3/index.json framework: net8.0 storage: none -nuget FSharp.Core ~> 8 -nuget Fable.Remoting.Giraffe ~> 5 -nuget Saturn ~> 0 nuget Expecto ~> 9 - -nuget Fable.Core ~> 4 -nuget Fable.Elmish ~> 4 -nuget Fable.Elmish.React ~> 4 -nuget Fable.Elmish.Debugger ~> 4 -nuget Fable.Elmish.HMR ~> 7 -nuget Fable.Mocha ~> 2 -nuget Fable.Remoting.Client ~> 7 -nuget Feliz ~> 2 +nuget SAFE.Server ~> 5-beta +nuget SAFE.Client ~> 5-beta nuget Fake.Core.Target ~> 5 nuget Fake.IO.FileSystem ~> 5 diff --git a/Content/default/paket.lock b/Content/default/paket.lock index 6f14a3fc..777d81ff 100644 --- a/Content/default/paket.lock +++ b/Content/default/paket.lock @@ -9,7 +9,7 @@ NUGET Fable.Browser.Blob (1.3) Fable.Core (>= 3.0) FSharp.Core (>= 4.7.2) - Fable.Browser.Dom (2.15) + Fable.Browser.Dom (2.16) Fable.Browser.Blob (>= 1.3) Fable.Browser.Event (>= 1.5) Fable.Browser.WebStorage (>= 1.2) @@ -31,7 +31,7 @@ NUGET Fable.Browser.Event (>= 1.5) Fable.Core (>= 3.0) FSharp.Core (>= 4.7.2) - Fable.Core (4.2) + Fable.Core (4.3) Fable.Elmish (4.1) Fable.Core (>= 3.7.1) FSharp.Core (>= 4.7.2) @@ -60,27 +60,27 @@ NUGET Fable.ReactDom.Types (18.2) Fable.React.Types (>= 18.3) FSharp.Core (>= 4.7.2) - Fable.Remoting.Client (7.30) + Fable.Remoting.Client (7.31) Fable.Browser.XMLHttpRequest (>= 1.0) Fable.Core (>= 3.1.5) - Fable.Remoting.MsgPack (>= 1.21) + Fable.Remoting.MsgPack (>= 1.24) Fable.SimpleJson (>= 3.24) FSharp.Core (>= 4.7.2) - Fable.Remoting.Giraffe (5.17) - Fable.Remoting.Server (>= 5.35) + Fable.Remoting.Giraffe (5.19) + Fable.Remoting.Server (>= 5.37) FSharp.Core (>= 6.0) Giraffe (>= 5.0) - Microsoft.IO.RecyclableMemoryStream (>= 2.2) + Microsoft.IO.RecyclableMemoryStream (>= 3.0 < 4.0) Fable.Remoting.Json (2.23) FSharp.Core (>= 6.0) Newtonsoft.Json (>= 12.0.2) - Fable.Remoting.MsgPack (1.21) + Fable.Remoting.MsgPack (1.24) FSharp.Core (>= 4.7.2) - Fable.Remoting.Server (5.35) + Fable.Remoting.Server (5.37) Fable.Remoting.Json (>= 2.23) - Fable.Remoting.MsgPack (>= 1.21) + Fable.Remoting.MsgPack (>= 1.24) FSharp.Core (>= 6.0) - Microsoft.IO.RecyclableMemoryStream (>= 2.2) + Microsoft.IO.RecyclableMemoryStream (>= 3.0 < 4.0) Fable.SimpleJson (3.24) Fable.Core (>= 3.1.5) Fable.Parsimmon (>= 4.0) @@ -122,7 +122,7 @@ NUGET Fake.IO.FileSystem (5.23.1) Fake.Core.String (>= 5.23.1) FSharp.Core (>= 6.0) - Farmer (1.8.4) + Farmer (1.8.5) FSharp.Core (>= 5.0) System.Text.Json (>= 5.0) Feliz (2.7) @@ -151,30 +151,44 @@ NUGET FSharp.Core (>= 5.0) Microsoft.AspNetCore.Authentication.JwtBearer (8.0.1) Microsoft.IdentityModel.Protocols.OpenIdConnect (>= 7.1.2) - Microsoft.IdentityModel.Abstractions (7.2) - Microsoft.IdentityModel.JsonWebTokens (7.2) - Microsoft.IdentityModel.Tokens (>= 7.2) - Microsoft.IdentityModel.Logging (7.2) - Microsoft.IdentityModel.Abstractions (>= 7.2) - Microsoft.IdentityModel.Protocols (7.2) - Microsoft.IdentityModel.Logging (>= 7.2) - Microsoft.IdentityModel.Tokens (>= 7.2) - Microsoft.IdentityModel.Protocols.OpenIdConnect (7.2) - Microsoft.IdentityModel.Protocols (>= 7.2) - System.IdentityModel.Tokens.Jwt (>= 7.2) - Microsoft.IdentityModel.Tokens (7.2) - Microsoft.IdentityModel.Logging (>= 7.2) - Microsoft.IO.RecyclableMemoryStream (2.3.2) + Microsoft.IdentityModel.Abstractions (7.3) + Microsoft.IdentityModel.JsonWebTokens (7.3) + Microsoft.IdentityModel.Tokens (>= 7.3) + Microsoft.IdentityModel.Logging (7.3) + Microsoft.IdentityModel.Abstractions (>= 7.3) + Microsoft.IdentityModel.Protocols (7.3) + Microsoft.IdentityModel.Logging (>= 7.3) + Microsoft.IdentityModel.Tokens (>= 7.3) + Microsoft.IdentityModel.Protocols.OpenIdConnect (7.3) + Microsoft.IdentityModel.Protocols (>= 7.3) + System.IdentityModel.Tokens.Jwt (>= 7.3) + Microsoft.IdentityModel.Tokens (7.3) + Microsoft.IdentityModel.Logging (>= 7.3) + Microsoft.IO.RecyclableMemoryStream (3.0) Mono.Cecil (0.11.5) Newtonsoft.Json (13.0.3) + SAFE.Client (5.0.0-beta1) + Fable.Core (>= 4.3 < 5.0) + Fable.Elmish (>= 4.1 < 5.0) + Fable.Elmish.Debugger (>= 4.0 < 5.0) + Fable.Elmish.HMR (>= 7.0 < 8.0) + Fable.Elmish.React (>= 4.0 < 5.0) + Fable.Mocha (>= 2.17 < 3.0) + Fable.Remoting.Client (>= 7.31 < 8.0) + Feliz (>= 2.7 < 3.0) + FSharp.Core (>= 8.0.101 < 9.0) + SAFE.Server (5.0.0-beta1) + Fable.Remoting.Giraffe (>= 5.19 < 6.0) + FSharp.Core (>= 8.0.101 < 9.0) + Saturn (>= 0.16.1 < 1.0) Saturn (0.16.1) FSharp.Control.Websockets (>= 0.2.2) Giraffe (>= 6.0) Microsoft.AspNetCore.Authentication.JwtBearer (>= 6.0.3) System.Collections.Immutable (8.0) - System.IdentityModel.Tokens.Jwt (7.2) - Microsoft.IdentityModel.JsonWebTokens (>= 7.2) - Microsoft.IdentityModel.Tokens (>= 7.2) + System.IdentityModel.Tokens.Jwt (7.3) + Microsoft.IdentityModel.JsonWebTokens (>= 7.3) + Microsoft.IdentityModel.Tokens (>= 7.3) System.Reactive (5.0) System.Text.Encodings.Web (8.0) System.Text.Json (8.0.1) diff --git a/Content/default/src/Client/Index.fs b/Content/default/src/Client/Index.fs index f6b74262..b12504ab 100644 --- a/Content/default/src/Client/Index.fs +++ b/Content/default/src/Client/Index.fs @@ -1,43 +1,46 @@ module Index open Elmish -open Fable.Remoting.Client +open SAFE open Shared -type Model = { Todos: Todo list; Input: string } +type Model = { + Todos: Deferred + Input: string +} type Msg = - | GotTodos of Todo list | SetInput of string - | AddTodo - | AddedTodo of Todo + | GetTodos of AsyncOperation + | AddTodo of AsyncOperation -let todosApi = - Remoting.createApi () - |> Remoting.withRouteBuilder Route.builder - |> Remoting.buildProxy +let todosApi = Api.makeProxy () let init () = - let model = { Todos = []; Input = "" } - let cmd = Cmd.OfAsync.perform todosApi.getTodos () GotTodos - model, cmd + let model = { Todos = NotStarted; Input = "" } + model, Cmd.ofMsg (GetTodos(Start())) let update msg model = match msg with - | GotTodos todos -> { model with Todos = todos }, Cmd.none | SetInput value -> { model with Input = value }, Cmd.none - | AddTodo -> - let todo = Todo.create model.Input - - let cmd = Cmd.OfAsync.perform todosApi.addTodo todo AddedTodo - - { model with Input = "" }, cmd - | AddedTodo todo -> - { - model with - Todos = model.Todos @ [ todo ] - }, - Cmd.none + | GetTodos msg -> + match msg with + | Start() -> + let cmd = Cmd.OfAsync.perform todosApi.getTodos () (Finished >> GetTodos) + { model with Todos = InProgress }, cmd + | Finished todos -> { model with Todos = Resolved todos }, Cmd.none + | AddTodo msg -> + match msg with + | Start() -> + let todo = Todo.create model.Input + let cmd = Cmd.OfAsync.perform todosApi.addTodo todo (Finished >> AddTodo) + { model with Input = "" }, cmd + | Finished todo -> + { + model with + Todos = model.Todos.Map(fun todos -> todos @ [ todo ]) + }, + Cmd.none open Feliz @@ -54,13 +57,13 @@ let private todoAction model dispatch = prop.onChange (SetInput >> dispatch) prop.onKeyPress (fun ev -> if ev.key = "Enter" then - dispatch AddTodo) + dispatch (AddTodo(Start()))) ] Html.button [ prop.className "flex-no-shrink p-2 px-12 rounded bg-teal-600 outline-none focus:ring-2 ring-teal-300 font-bold text-white hover:bg-teal disabled:opacity-30 disabled:cursor-not-allowed" prop.disabled (Todo.isValid model.Input |> not) - prop.onClick (fun _ -> dispatch AddTodo) + prop.onClick (fun _ -> dispatch (AddTodo(Start()))) prop.text "Add" ] ] @@ -73,8 +76,12 @@ let private todoList model dispatch = Html.ol [ prop.className "list-decimal ml-6" prop.children [ - for todo in model.Todos do - Html.li [ prop.className "my-1"; prop.text todo.Description ] + match model.Todos with + | NotStarted -> Html.text "Not Started." + | InProgress -> Html.text "Loading..." + | Resolved todos -> + for todo in todos do + Html.li [ prop.className "my-1"; prop.text todo.Description ] ] ] diff --git a/Content/default/src/Client/paket.references b/Content/default/src/Client/paket.references index 6a9d1732..8f1133a1 100644 --- a/Content/default/src/Client/paket.references +++ b/Content/default/src/Client/paket.references @@ -1,8 +1 @@ -Fable.Core -Fable.Elmish -Fable.Elmish.React -Fable.Elmish.Debugger -Fable.Elmish.HMR -Fable.Remoting.Client -Feliz -FSharp.Core +SAFE.Client \ No newline at end of file diff --git a/Content/default/src/Server/Server.fs b/Content/default/src/Server/Server.fs index 20f76a34..e6f2ca7f 100644 --- a/Content/default/src/Server/Server.fs +++ b/Content/default/src/Server/Server.fs @@ -1,9 +1,7 @@ module Server -open Fable.Remoting.Server -open Fable.Remoting.Giraffe +open SAFE open Saturn - open Shared module Storage = @@ -21,7 +19,7 @@ module Storage = addTodo (Todo.create "Write your app") |> ignore addTodo (Todo.create "Ship it!!!") |> ignore -let todosApi = { +let todosApi ctx = { getTodos = fun () -> async { return Storage.todos |> List.ofSeq } addTodo = fun todo -> async { @@ -32,11 +30,7 @@ let todosApi = { } } -let webApp = - Remoting.createApi () - |> Remoting.withRouteBuilder Route.builder - |> Remoting.fromValue todosApi - |> Remoting.buildHttpHandler +let webApp = Api.make todosApi let app = application { use_router webApp diff --git a/Content/default/src/Server/paket.references b/Content/default/src/Server/paket.references index 55042ded..f89726aa 100644 --- a/Content/default/src/Server/paket.references +++ b/Content/default/src/Server/paket.references @@ -1,3 +1 @@ -Saturn -Fable.Remoting.Giraffe -FSharp.Core \ No newline at end of file +SAFE.Server \ No newline at end of file