SAFE Stack の ToDo アプリはデータを保存できるようにする続き。

データ保存の API をサーバで提供 (link) するになったので、これをクライアントから利用できるようにした。 セーブの状態を SaveState 型で管理するようにしている。 他の処理は既存の Add (ToDo 項目の追加) のやり方を参考した。 (これはサーバの API 周りも同様)

diff --git a/src/Client/Index.fs b/src/Client/Index.fs
--- a/src/Client/Index.fs
+++ b/src/Client/Index.fs
@@ -14,6 +14,8 @@ let (|RE|_|) re str =
 
 type Msg =
     | GotTodos of Todo list
+    | SaveTodos
+    | SavedTodos of Todo List
     | SetInput of string
     | AddTodo
     | AddedTodo of Todo
@@ -36,9 +38,15 @@ let logMessage (msg: Msg) =
     ()
 #endif  // DEBUG
 
+type SaveState =
+    | Saved
+    | Saving
+    | ToBeSaved
+
 type Model = {
     Todos: Todo list;
     Input: string;
+    SaveState: SaveState;
     DragFrom: string option;
     DragOn:   string option;
 }
@@ -52,6 +60,7 @@ let init () : Model * Cmd<Msg> =
     let model = {
         Todos = [];
         Input = "";
+        SaveState = Saving;
         DragFrom = None;
         DragOn   = None;
     }
@@ -95,7 +104,23 @@ let dropTarget_label2posOpt (label: string) =
 let update (msg: Msg) (model: Model) : Model * Cmd<Msg> =
     logMessage msg
     match msg with
-    | GotTodos todos -> { model with Todos = todos }, Cmd.none
+    | GotTodos todos ->
+        { model with
+              Todos = todos;
+              SaveState = Saved;
+        },
+        Cmd.none
+    | SaveTodos ->
+        { model with
+              SaveState = Saving
+        },
+        Cmd.OfAsync.perform todosApi.saveTodos () SavedTodos
+    | SavedTodos todos ->
+        { model with
+              Todos = todos;
+              SaveState = Saved;
+        },
+        Cmd.none
     | SetInput value -> { model with Input = value }, Cmd.none
     | AddTodo ->
         let todo = Todo.create model.Input
@@ -106,7 +131,9 @@ let update (msg: Msg) (model: Model) : Model * Cmd<Msg> =
         { model with Input = "" }, cmd
     | AddedTodo todo ->
         { model with
-              Todos = model.Todos @ [ todo ] },
+              Todos = model.Todos @ [ todo ];
+              SaveState = ToBeSaved;
+        },
         Cmd.none
     | MoveTodo (srcLabel, dstLabel) ->
         // ToDo: implement: more gentle error handling
@@ -128,7 +155,9 @@ let update (msg: Msg) (model: Model) : Model * Cmd<Msg> =
         cmd
     | MovedTodo todos ->
         { model with
-              Todos = todos; },
+              Todos = todos;
+              SaveState = ToBeSaved;
+        },
         Cmd.none
     | DragStart label ->
         { model with
@@ -260,6 +289,14 @@ let containerBox (model: Model) (dispatch: Msg -> unit) =
                 ]
             ]
         ]
+        Bulma.control.p [
+            Bulma.button.a [
+                color.isPrimary
+                prop.disabled (model.SaveState <> ToBeSaved)
+                prop.onClick (fun _ -> dispatch SaveTodos)
+                prop.text "Save"
+            ]
+        ]
     ]
 
 let view (model: Model) (dispatch: Msg -> unit) =