Skip to content

Commit 859f08f

Browse files
authored
Merge pull request #451 from SAFE-Stack/use-elmish
Use elmish
2 parents 61b8777 + 04f6f5a commit 859f08f

9 files changed

Lines changed: 79 additions & 93 deletions

File tree

package-lock.json

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"dependencies": {
99
"react": "^18.2.0",
1010
"react-dom": "^18.2.0",
11-
"sweetalert2": "^8.19.1"
11+
"sweetalert2": "^8.19.1",
12+
"use-sync-external-store": "^1.2.0"
1213
},
1314
"devDependencies": {
1415
"@types/node": "^20.9.1",

paket.dependencies

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ nuget Azure.Storage.Blobs
88
nuget Elmish.SweetAlert
99
nuget Feliz.DaisyUI
1010
nuget Feliz.Router
11+
nuget Feliz.UseElmish
1112
nuget FSharp.Core ~> 8
1213
nuget Fable.Remoting.Giraffe ~> 5
1314
nuget FSToolkit.ErrorHandling

paket.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ NUGET
168168
Fable.Elmish (>= 4.0)
169169
Feliz (>= 2.3)
170170
FSharp.Core (>= 4.7.2)
171+
Feliz.UseElmish (2.5)
172+
Fable.Elmish (>= 4.0)
173+
FSharp.Core (>= 4.7.2)
171174
FParsec (1.1.1)
172175
FSharp.Core (>= 4.3.4)
173176
FSharp.Control.Reactive (5.0.5)

src/Client/Index.fs

Lines changed: 23 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ open Page
1010
open Shared
1111

1212
type PageTab =
13-
| Home of Home.Model
14-
| Login of Login.Model
15-
| Wishlist of WishList.Model
13+
| Home
14+
| Login
15+
| Wishlist of UserData
1616
| NotFound
1717

1818
type User =
@@ -22,107 +22,51 @@ type User =
2222
type Model = { Page: PageTab; User: User }
2323

2424
type Msg =
25-
| HomePageMsg of Home.Msg
26-
| LoginPageMsg of Login.Msg
27-
| WishlistMsg of WishList.Msg
2825
| UrlChanged of string list
2926
| OnSessionChange
3027
| Logout
3128

32-
let wishListApi token =
33-
let bearer = $"Bearer {token}"
34-
35-
Remoting.createApi ()
36-
|> Remoting.withAuthorizationHeader bearer
37-
|> Remoting.withRouteBuilder Route.builder
38-
|> Remoting.buildProxy<IWishListApi>
3929

4030
let guestApi =
4131
Remoting.createApi ()
4232
|> Remoting.withRouteBuilder Route.builder
4333
|> Remoting.buildProxy<IGuestApi>
4434

4535
let initFromUrl model url =
36+
let loggedInUser =
37+
Session.loadUser () |> Option.map User |> Option.defaultValue Guest
38+
4639
match url with
4740
| [] ->
48-
let homeModel, homeMsg = Home.init guestApi
49-
50-
let model = {
51-
Page = Home homeModel
52-
User = model.User
53-
}
54-
55-
let cmd = homeMsg |> Cmd.map HomePageMsg
56-
model, cmd
41+
let model = { Page = Home; User = loggedInUser }
42+
model, Cmd.none
5743
| [ "login" ] ->
58-
let loginModel, loginMsg = Login.init ()
59-
60-
let model = {
61-
Page = Login loginModel
62-
User = model.User
63-
}
64-
65-
let cmd = loginMsg |> Cmd.map LoginPageMsg
66-
model, cmd
44+
let model = { Page = Login; User = loggedInUser }
45+
model, Cmd.none
6746
| [ "wishlist" ] ->
68-
match model.User with
47+
match loggedInUser with
6948
| User user ->
70-
let wishlistModel, wishlistMsg =
71-
WishList.init (wishListApi user.Token) user.UserName
72-
7349
let model = {
74-
Page = Wishlist wishlistModel
75-
User = model.User
50+
Page = Wishlist user
51+
User = loggedInUser
7652
}
7753

78-
let cmd = wishlistMsg |> Cmd.map WishlistMsg
79-
model, cmd
54+
model, Cmd.none
8055
| Guest -> model, Cmd.navigate "login"
81-
| _ -> { Page = NotFound; User = model.User }, Cmd.none
56+
| _ -> { Page = NotFound; User = loggedInUser }, Cmd.none
8257

8358
let init () =
84-
let model, _ = Home.init guestApi
8559
let user = Session.loadUser () |> Option.map User |> Option.defaultValue Guest
8660

87-
Router.currentUrl () |> initFromUrl { Page = Home model; User = user }
61+
Router.currentUrl () |> initFromUrl { Page = Home; User = user }
8862

8963
let update msg model =
90-
match model.Page, msg with
91-
| Home homeModel, HomePageMsg homeMsg ->
92-
let newModel, cmd = Home.update homeMsg homeModel
93-
94-
{
95-
Page = Home newModel
96-
User = model.User
97-
},
98-
cmd
99-
| Login loginModel, LoginPageMsg loginMsg ->
100-
let user =
101-
match loginMsg with
102-
| Login.LoggedIn user -> User user
103-
| _ -> model.User
104-
105-
let newModel, cmd = Login.update guestApi loginMsg loginModel
106-
{ Page = Login newModel; User = user }, cmd |> Cmd.map LoginPageMsg
107-
| Wishlist wishlistModel, WishlistMsg wishlistMsg ->
108-
let token =
109-
match model.User with
110-
| User data -> data.Token
111-
| Guest -> ""
112-
113-
let newModel, cmd = WishList.update (wishListApi token) wishlistMsg wishlistModel
114-
115-
{
116-
Page = Wishlist newModel
117-
User = model.User
118-
},
119-
cmd |> Cmd.map WishlistMsg
120-
| NotFound, _ -> { Page = NotFound; User = model.User }, Cmd.none
121-
| _, UrlChanged url -> initFromUrl model url
122-
| _, Logout ->
64+
match msg with
65+
| UrlChanged url -> initFromUrl model url
66+
| Logout ->
12367
Session.deleteUser ()
12468
{ model with User = Guest }, Cmd.navigate ""
125-
| _, OnSessionChange ->
69+
| OnSessionChange ->
12670
let session = Session.loadUser ()
12771
let user = session |> Option.map User |> Option.defaultValue Guest
12872

@@ -132,7 +76,6 @@ let update msg model =
13276
|> Option.defaultValue (Cmd.navigate "login")
13377

13478
{ model with User = user }, cmd
135-
| _, _ -> model, Cmd.none
13679

13780
open Feliz
13881

@@ -177,9 +120,9 @@ let view model dispatch =
177120
prop.className "overflow-y-auto"
178121
prop.children [
179122
match model.Page with
180-
| Home homeModel -> Home.view homeModel (HomePageMsg >> dispatch)
181-
| Login loginModel -> Login.view loginModel (LoginPageMsg >> dispatch)
182-
| Wishlist wishlistModel -> WishList.view wishlistModel (WishlistMsg >> dispatch)
123+
| Home -> Home.View guestApi
124+
| Login -> Login.View guestApi
125+
| Wishlist user -> WishList.View user
183126
| NotFound -> Html.div [ prop.text "Not Found" ]
184127
]
185128
]

src/Client/pages/Home.fs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module Page.Home
22

33
open Elmish
44
open Feliz.DaisyUI
5+
open Feliz.UseElmish
56
open Shared
67

78
type Model = { Books: Book seq }
@@ -43,7 +44,10 @@ let bookRow book =
4344
]
4445
]
4546

46-
let view model dispatch =
47+
[<ReactComponent>]
48+
let View api =
49+
let model, dispatch = React.useElmish ((fun () -> init api), update, [||])
50+
4751
Html.div [
4852
prop.className "overflow-y-auto"
4953
prop.children [

src/Client/pages/Login.fs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ open System
44
open Elmish
55
open Feliz.DaisyUI
66
open Feliz.Router
7+
open Feliz.UseElmish
78
open FsToolkit.ErrorHandling
89
open Shared
910
open SAFE
@@ -68,7 +69,10 @@ let update (guestApi: IGuestApi) msg model =
6869

6970
open Feliz
7071

71-
let view model dispatch =
72+
[<ReactComponent>]
73+
let View guestApi =
74+
let model, dispatch = React.useElmish (init, (update guestApi), [||])
75+
7276
Html.div [
7377
prop.className "grid justify-center"
7478
prop.children [

src/Client/pages/WishList.fs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,18 @@ open System
44
open Elmish
55
open Elmish.SweetAlert
66
open Feliz.DaisyUI
7+
open Feliz.UseElmish
78
open Shared
89
open SAFE
10+
open Fable.Remoting.Client
11+
12+
let wishListApi token =
13+
let bearer = $"Bearer {token}"
14+
15+
Remoting.createApi ()
16+
|> Remoting.withAuthorizationHeader bearer
17+
|> Remoting.withRouteBuilder Route.builder
18+
|> Remoting.buildProxy<IWishListApi>
919

1020
type Model = {
1121
Wishlist: WishList
@@ -26,10 +36,10 @@ type Msg =
2636
let alert message alertType =
2737
SimpleAlert(message).Type(alertType) |> SweetAlert.Run
2838

29-
let init (wishListApi: IWishListApi) (userName: UserName) =
39+
let init api (user: UserData) =
3040
let model = {
3141
Wishlist = {
32-
UserName = userName
42+
UserName = user.UserName
3343
Books = List.empty
3444
}
3545
LastResetTime = DateTime.MinValue
@@ -38,21 +48,21 @@ let init (wishListApi: IWishListApi) (userName: UserName) =
3848

3949
let cmd =
4050
Cmd.batch [
41-
Cmd.OfAsync.either wishListApi.getWishlist userName GotWishlist UnhandledError
42-
Cmd.OfAsync.either wishListApi.getLastResetTime () GotLastRestTime UnhandledError
51+
Cmd.OfAsync.either api.getWishlist user.UserName GotWishlist UnhandledError
52+
Cmd.OfAsync.either api.getLastResetTime () GotLastRestTime UnhandledError
4353
]
4454

4555
model, cmd
4656

47-
let update booksApi msg model =
57+
let update api msg model =
4858
match msg with
4959
| GotLastRestTime time -> { model with LastResetTime = time }, Cmd.none
5060
| GotWishlist wishlist -> { model with Wishlist = wishlist }, Cmd.none
5161
| RemoveBook title ->
5262
let userName = model.Wishlist.UserName
5363

5464
let cmd =
55-
Cmd.OfAsync.either booksApi.removeBook (userName, title) RemovedBook UnhandledError
65+
Cmd.OfAsync.either api.removeBook (userName, title) RemovedBook UnhandledError
5666

5767
model, cmd
5868
| RemovedBook title ->
@@ -71,7 +81,7 @@ let update booksApi msg model =
7181
match model.Wishlist.VerifyNewBookIsNotADuplicate book with
7282
| Ok _ ->
7383
let userName = model.Wishlist.UserName
74-
model, Cmd.OfAsync.either booksApi.addBook (userName, book) AddedBook UnhandledError
84+
model, Cmd.OfAsync.either api.addBook (userName, book) AddedBook UnhandledError
7585
| Error error -> model, Exception(error) |> UnhandledError |> Cmd.ofMsg
7686
| NewBook.Cancel, _ -> { model with NewBook = None }, Cmd.none
7787
| _, Some newBook ->
@@ -159,7 +169,11 @@ let table model dispatch =
159169
]
160170
]
161171

162-
let view model dispatch =
172+
[<ReactComponent>]
173+
let View user =
174+
let api: IWishListApi = wishListApi user.Token
175+
176+
let model, dispatch = React.useElmish ((fun () -> init api user), update api, [||])
163177
let user = model.Wishlist.UserName.Value
164178
let lastReset = model.LastResetTime.ToString("yyyy-MM-dd HH:mm")
165179

src/Client/paket.references

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ Feliz.DaisyUI
1010
Feliz.Router
1111
FSToolkit.ErrorHandling
1212
Elmish.SweetAlert
13-
Fable.SimpleJson
13+
Fable.SimpleJson
14+
Feliz.UseElmish

0 commit comments

Comments
 (0)