Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to beta5 #605

Merged
merged 9 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 30 additions & 38 deletions Content/default/paket.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ NUGET
Expecto (9.0.4)
FSharp.Core (>= 4.6)
Mono.Cecil (>= 0.11.3)
Fable.AST (4.3)
Fable.AST (4.4)
Fable.Browser.Blob (1.3)
Fable.Core (>= 3.0)
FSharp.Core (>= 4.7.2)
Expand Down Expand Up @@ -35,10 +35,6 @@ NUGET
Fable.Elmish (4.1)
Fable.Core (>= 3.7.1)
FSharp.Core (>= 4.7.2)
Fable.Elmish.Debugger (4.0)
Fable.Elmish (>= 4.0)
FSharp.Core (>= 6.0.7)
Thoth.Json (>= 6.0)
Fable.Elmish.HMR (7.0)
Fable.Core (>= 3.7.1)
Fable.Elmish.React (>= 4.0)
Expand All @@ -60,7 +56,7 @@ NUGET
Fable.ReactDom.Types (18.2)
Fable.React.Types (>= 18.3)
FSharp.Core (>= 4.7.2)
Fable.Remoting.Client (7.31)
Fable.Remoting.Client (7.32)
Fable.Browser.XMLHttpRequest (>= 1.0)
Fable.Core (>= 3.1.5)
Fable.Remoting.MsgPack (>= 1.24)
Expand Down Expand Up @@ -122,7 +118,7 @@ NUGET
Fake.IO.FileSystem (5.23.1)
Fake.Core.String (>= 5.23.1)
FSharp.Core (>= 6.0)
Farmer (1.8.5)
Farmer (1.8.9)
FSharp.Core (>= 5.0)
System.Text.Json (>= 5.0)
Feliz (2.7)
Expand All @@ -137,62 +133,58 @@ NUGET
FSharp.Control.Reactive (5.0.5)
FSharp.Core (>= 4.7.2)
System.Reactive (>= 5.0 < 6.0)
FSharp.Control.Websockets (0.2.3)
FSharp.Control.Websockets (0.3)
FSharp.Core (>= 6.0)
Microsoft.IO.RecyclableMemoryStream (>= 2.2.1)
FSharp.Core (8.0.101)
Giraffe (6.2)
Microsoft.IO.RecyclableMemoryStream (>= 3.0)
FSharp.Core (8.0.200)
Giraffe (6.3)
FSharp.Core (>= 6.0)
Giraffe.ViewEngine (>= 1.4)
Microsoft.IO.RecyclableMemoryStream (>= 2.2.1)
Newtonsoft.Json (>= 13.0.3)
System.Text.Json (>= 7.0.3)
System.Text.Json (>= 7.0.4)
Giraffe.ViewEngine (1.4)
FSharp.Core (>= 5.0)
Microsoft.AspNetCore.Authentication.JwtBearer (8.0.1)
Microsoft.AspNetCore.Authentication.JwtBearer (8.0.3)
Microsoft.IdentityModel.Protocols.OpenIdConnect (>= 7.1.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.IdentityModel.Abstractions (7.5)
Microsoft.IdentityModel.JsonWebTokens (7.5)
Microsoft.IdentityModel.Tokens (>= 7.5)
Microsoft.IdentityModel.Logging (7.5)
Microsoft.IdentityModel.Abstractions (>= 7.5)
Microsoft.IdentityModel.Protocols (7.5)
Microsoft.IdentityModel.Tokens (>= 7.5)
Microsoft.IdentityModel.Protocols.OpenIdConnect (7.5)
Microsoft.IdentityModel.Protocols (>= 7.5)
System.IdentityModel.Tokens.Jwt (>= 7.5)
Microsoft.IdentityModel.Tokens (7.5)
Microsoft.IdentityModel.Logging (>= 7.5)
Microsoft.IO.RecyclableMemoryStream (3.0)
Mono.Cecil (0.11.5)
Newtonsoft.Json (13.0.3)
SAFE.Client (5.0.0-beta1)
SAFE.Client (5.0.0-beta5)
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)
Fable.SimpleJson (>= 3.24)
Feliz (>= 2.7 < 3.0)
FSharp.Core (>= 8.0.101 < 9.0)
SAFE.Server (5.0.0-beta1)
FSharp.Core (>= 8.0.200 < 9.0)
SAFE.Server (5.0.0-beta5)
Fable.Remoting.Giraffe (>= 5.19 < 6.0)
FSharp.Core (>= 8.0.101 < 9.0)
FSharp.Core (>= 8.0.200 < 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.3)
Microsoft.IdentityModel.JsonWebTokens (>= 7.3)
Microsoft.IdentityModel.Tokens (>= 7.3)
System.IdentityModel.Tokens.Jwt (7.5)
Microsoft.IdentityModel.JsonWebTokens (>= 7.5)
Microsoft.IdentityModel.Tokens (>= 7.5)
System.Reactive (5.0)
System.Text.Encodings.Web (8.0)
System.Text.Json (8.0.1)
System.Text.Json (8.0.3)
System.Text.Encodings.Web (>= 8.0)
Thoth.Json (10.2)
Fable.Core (>= 3.6.2)
FSharp.Core (>= 4.7.2)
114 changes: 60 additions & 54 deletions Content/default/src/Client/Index.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,89 +5,95 @@ open SAFE
open Shared

type Model = {
Todos: Deferred<Todo list>
Todos: RemoteData<Todo list>
Input: string
}

type Msg =
| SetInput of string
| GetTodos of AsyncOperation<unit, Todo list>
| AddTodo of AsyncOperation<unit, Todo>
| LoadTodos of ApiCall<unit, Todo list>
| SaveTodo of ApiCall<string, Todo>

let todosApi = Api.makeProxy<ITodosApi> ()

let init () =
let model = { Todos = NotStarted; Input = "" }
model, Cmd.ofMsg (GetTodos(Start()))
let initialModel = { Todos = NotStarted; Input = "" }
let initialCmd = LoadTodos(Start()) |> Cmd.ofMsg

initialModel, initialCmd

let update msg model =
match msg with
| SetInput value -> { model with Input = value }, Cmd.none
| GetTodos msg ->
| LoadTodos 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 ->
let loadTodosCmd = Cmd.OfAsync.perform todosApi.getTodos () (Finished >> LoadTodos)

{ model with Todos = Loading }, loadTodosCmd
| Finished todos -> { model with Todos = Loaded todos }, Cmd.none
| SaveTodo 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
| Start todoText ->
let saveTodoCmd =
let todo = Todo.create todoText
Cmd.OfAsync.perform todosApi.addTodo todo (Finished >> SaveTodo)

{ model with Input = "" }, saveTodoCmd
| Finished todo ->
{
model with
Todos = model.Todos.Map(fun todos -> todos @ [ todo ])
Todos = model.Todos |> RemoteData.map (fun todos -> todos @ [ todo ])
},
Cmd.none

open Feliz

let private todoAction model dispatch =
Html.div [
prop.className "flex flex-col sm:flex-row mt-4 gap-4"
prop.children [
Html.input [
prop.className
"shadow appearance-none border rounded w-full py-2 px-3 outline-none focus:ring-2 ring-teal-300 text-grey-darker"
prop.value model.Input
prop.placeholder "What needs to be done?"
prop.autoFocus true
prop.onChange (SetInput >> dispatch)
prop.onKeyPress (fun ev ->
if ev.key = "Enter" then
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(Start())))
prop.text "Add"
module ViewComponents =
let todoAction model dispatch =
Html.div [
prop.className "flex flex-col sm:flex-row mt-4 gap-4"
prop.children [
Html.input [
prop.className
"shadow appearance-none border rounded w-full py-2 px-3 outline-none focus:ring-2 ring-teal-300 text-grey-darker"
prop.value model.Input
prop.placeholder "What needs to be done?"
prop.autoFocus true
prop.onChange (SetInput >> dispatch)
prop.onKeyPress (fun ev ->
if ev.key = "Enter" then
dispatch (SaveTodo(Start model.Input)))
]
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 (SaveTodo(Start model.Input)))
prop.text "Add"
]
]
]
]

let private todoList model dispatch =
Html.div [
prop.className "bg-white/80 rounded-md shadow-md p-4 w-5/6 lg:w-3/4 lg:max-w-2xl"
prop.children [
Html.ol [
prop.className "list-decimal ml-6"
prop.children [
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 ]
let todoList model dispatch =
Html.div [
prop.className "bg-white/80 rounded-md shadow-md p-4 w-5/6 lg:w-3/4 lg:max-w-2xl"
prop.children [
Html.ol [
prop.className "list-decimal ml-6"
prop.children [
match model.Todos with
| NotStarted -> Html.text "Not Started."
| Loading -> Html.text "Loading..."
| Loaded todos ->
for todo in todos do
Html.li [ prop.className "my-1"; prop.text todo.Description ]
]
]
]

todoAction model dispatch
todoAction model dispatch
]
]
]

let view model dispatch =
Html.section [
Expand All @@ -112,7 +118,7 @@ let view model dispatch =
prop.className "text-center text-5xl font-bold text-white mb-3 rounded-md p-4"
prop.text "SAFE.App"
]
todoList model dispatch
ViewComponents.todoList model dispatch
]
]
]
Expand Down
12 changes: 6 additions & 6 deletions Content/default/src/Server/Server.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ open Saturn
open Shared

module Storage =
let todos = ResizeArray()
let todos =
ResizeArray [
Todo.create "Create new SAFE project"
Todo.create "Write your app"
Todo.create "Ship it!!!"
]

let addTodo todo =
if Todo.isValid todo.Description then
Expand All @@ -14,11 +19,6 @@ module Storage =
else
Error "Invalid todo"

do
addTodo (Todo.create "Create new SAFE project") |> ignore
addTodo (Todo.create "Write your app") |> ignore
addTodo (Todo.create "Ship it!!!") |> ignore

let todosApi ctx = {
getTodos = fun () -> async { return Storage.todos |> List.ofSeq }
addTodo =
Expand Down
4 changes: 0 additions & 4 deletions Content/default/src/Shared/Shared.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ module Todo =
Description = description
}

module Route =
let builder typeName methodName =
sprintf "/api/%s/%s" typeName methodName

type ITodosApi = {
getTodos: unit -> Async<Todo list>
addTodo: Todo -> Async<Todo>
Expand Down
16 changes: 13 additions & 3 deletions Content/default/tests/Client/Client.Tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ open Fable.Mocha

open Index
open Shared
open SAFE

let client =
testList "Client" [
Expand All @@ -12,10 +13,19 @@ let client =
let newTodo = Todo.create "new todo"
let model, _ = init ()

let model, _ = update (AddedTodo newTodo) model
let model, _ = update (SaveTodo(Finished newTodo)) model

Expect.equal model.Todos.Length 1 "There should be 1 todo"
Expect.equal model.Todos.[0] newTodo "Todo should equal new todo"
Expect.equal
(model.Todos |> RemoteData.map _.Length |> RemoteData.defaultValue 0)
1
"There should be 1 todo"

Expect.equal
(model.Todos
|> RemoteData.map List.head
|> RemoteData.defaultValue (Todo.create ""))
newTodo
"Todo should equal new todo"
]

let all =
Expand Down