From cdcc8210fe4bb39fbc3556d22493671c64cafa72 Mon Sep 17 00:00:00 2001
From: Eike Kettner <eike.kettner@posteo.de>
Date: Mon, 23 Nov 2020 09:27:05 +0100
Subject: [PATCH] Throttle customfield update requests

---
 .../src/main/elm/Comp/CustomFieldInput.elm    | 18 ++--
 .../main/elm/Comp/CustomFieldMultiInput.elm   | 19 ++--
 .../src/main/elm/Comp/ItemDetail/EditMenu.elm |  5 +-
 .../src/main/elm/Comp/ItemDetail/Model.elm    |  6 +-
 .../src/main/elm/Comp/ItemDetail/Update.elm   | 97 +++++++++++++++----
 .../src/main/elm/Comp/ItemDetail/View.elm     |  6 +-
 6 files changed, 104 insertions(+), 47 deletions(-)

diff --git a/modules/webapp/src/main/elm/Comp/CustomFieldInput.elm b/modules/webapp/src/main/elm/Comp/CustomFieldInput.elm
index d9c4fa48..e49177db 100644
--- a/modules/webapp/src/main/elm/Comp/CustomFieldInput.elm
+++ b/modules/webapp/src/main/elm/Comp/CustomFieldInput.elm
@@ -175,7 +175,6 @@ type alias UpdateResult =
     { model : Model
     , cmd : Cmd Msg
     , result : FieldResult
-    , subs : Sub Msg
     }
 
 
@@ -219,7 +218,7 @@ update msg model =
                 model_ =
                     { model | fieldModel = TextField (Just str) }
             in
-            UpdateResult model_ Cmd.none (Value str) Sub.none
+            UpdateResult model_ Cmd.none (Value str)
 
         ( NumberMsg str, NumberField _ ) ->
             let
@@ -229,7 +228,7 @@ update msg model =
                 model_ =
                     { model | fieldModel = NumberField fm }
             in
-            UpdateResult model_ Cmd.none res Sub.none
+            UpdateResult model_ Cmd.none res
 
         ( MoneyMsg str, MoneyField _ ) ->
             let
@@ -242,7 +241,7 @@ update msg model =
                 model_ =
                     { model | fieldModel = MoneyField fm }
             in
-            UpdateResult model_ Cmd.none res Sub.none
+            UpdateResult model_ Cmd.none res
 
         ( ToggleBool, BoolField b ) ->
             let
@@ -259,7 +258,7 @@ update msg model =
                     else
                         "false"
             in
-            UpdateResult model_ Cmd.none (Value value) Sub.none
+            UpdateResult model_ Cmd.none (Value value)
 
         ( DateMsg lm, DateField _ picker ) ->
             let
@@ -280,14 +279,14 @@ update msg model =
                 model_ =
                     { model | fieldModel = DateField newDate picker_ }
             in
-            UpdateResult model_ Cmd.none value Sub.none
+            UpdateResult model_ Cmd.none value
 
         ( Remove, _ ) ->
-            UpdateResult model Cmd.none RemoveField Sub.none
+            UpdateResult model Cmd.none RemoveField
 
         -- no other possibilities, not well encoded here
         _ ->
-            UpdateResult model Cmd.none NoResult Sub.none
+            UpdateResult model Cmd.none NoResult
 
 
 mkLabel : Model -> String
@@ -396,7 +395,8 @@ makeInput icon model =
 
         DateField v dp ->
             div [ class "ui action left icon input" ]
-                [ Html.map DateMsg (Comp.DatePicker.view v Comp.DatePicker.defaultSettings dp)
+                [ Html.map DateMsg
+                    (Comp.DatePicker.view v Comp.DatePicker.defaultSettings dp)
                 , removeButton ""
                 , i [ class (iconOr <| Icons.customFieldType Data.CustomFieldType.Date) ] []
                 ]
diff --git a/modules/webapp/src/main/elm/Comp/CustomFieldMultiInput.elm b/modules/webapp/src/main/elm/Comp/CustomFieldMultiInput.elm
index 21ee078f..9d4e2cdb 100644
--- a/modules/webapp/src/main/elm/Comp/CustomFieldMultiInput.elm
+++ b/modules/webapp/src/main/elm/Comp/CustomFieldMultiInput.elm
@@ -138,7 +138,6 @@ mkFieldSelect fields =
 type alias UpdateResult =
     { model : Model
     , cmd : Cmd Msg
-    , subs : Sub Msg
     , result : CustomFieldChange
     }
 
@@ -152,7 +151,7 @@ update : Msg -> Model -> UpdateResult
 update msg model =
     case msg of
         CreateNewField ->
-            UpdateResult model Cmd.none Sub.none FieldCreateNew
+            UpdateResult model Cmd.none FieldCreateNew
 
         CustomFieldResp (Ok list) ->
             let
@@ -162,10 +161,10 @@ update msg model =
                         , fieldSelect = mkFieldSelect (currentOptions list.items model.visibleFields)
                     }
             in
-            UpdateResult model_ Cmd.none Sub.none NoFieldChange
+            UpdateResult model_ Cmd.none NoFieldChange
 
         CustomFieldResp (Err _) ->
-            UpdateResult model Cmd.none Sub.none NoFieldChange
+            UpdateResult model Cmd.none NoFieldChange
 
         FieldSelectMsg lm ->
             let
@@ -188,7 +187,7 @@ update msg model =
                     update (ApplyField field) model
 
                 Nothing ->
-                    UpdateResult model_ Cmd.none Sub.none NoFieldChange
+                    UpdateResult model_ Cmd.none NoFieldChange
 
         ApplyField f ->
             let
@@ -218,7 +217,7 @@ update msg model =
                 cmd_ =
                     Cmd.map (CustomFieldInputMsg f) fc
             in
-            UpdateResult model_ cmd_ Sub.none NoFieldChange
+            UpdateResult model_ cmd_ NoFieldChange
 
         RemoveField f ->
             let
@@ -231,7 +230,7 @@ update msg model =
                         , fieldSelect = mkFieldSelect (currentOptions model.allFields visible)
                     }
             in
-            UpdateResult model_ Cmd.none Sub.none (FieldValueRemove f)
+            UpdateResult model_ Cmd.none (FieldValueRemove f)
 
         CustomFieldInputMsg f lm ->
             let
@@ -268,10 +267,10 @@ update msg model =
                         update (RemoveField field) model_
 
                     else
-                        UpdateResult model_ cmd_ Sub.none result
+                        UpdateResult model_ cmd_ result
 
                 Nothing ->
-                    UpdateResult model Cmd.none Sub.none NoFieldChange
+                    UpdateResult model Cmd.none NoFieldChange
 
         SetValues values ->
             let
@@ -299,7 +298,7 @@ update msg model =
                         , visibleFields = modelDict
                     }
             in
-            UpdateResult model_ (Cmd.batch cmdList) Sub.none NoFieldChange
+            UpdateResult model_ (Cmd.batch cmdList) NoFieldChange
 
 
 
diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/EditMenu.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/EditMenu.elm
index 40f0c13a..6ac7b7f5 100644
--- a/modules/webapp/src/main/elm/Comp/ItemDetail/EditMenu.elm
+++ b/modules/webapp/src/main/elm/Comp/ItemDetail/EditMenu.elm
@@ -564,9 +564,6 @@ update flags msg model =
                 cmd_ =
                     Cmd.map CustomFieldMsg res.cmd
 
-                sub_ =
-                    Sub.map CustomFieldMsg res.subs
-
                 change =
                     case res.result of
                         NoFieldChange ->
@@ -581,7 +578,7 @@ update flags msg model =
                         FieldCreateNew ->
                             NoFormChange
             in
-            UpdateResult model_ cmd_ sub_ change
+            UpdateResult model_ cmd_ Sub.none change
 
 
 nameThrottleSub : Model -> Sub Msg
diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/Model.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/Model.elm
index 8f5bbf67..031df3ee 100644
--- a/modules/webapp/src/main/elm/Comp/ItemDetail/Model.elm
+++ b/modules/webapp/src/main/elm/Comp/ItemDetail/Model.elm
@@ -13,6 +13,7 @@ module Comp.ItemDetail.Model exposing
     )
 
 import Api.Model.BasicResult exposing (BasicResult)
+import Api.Model.CustomField exposing (CustomField)
 import Api.Model.EquipmentList exposing (EquipmentList)
 import Api.Model.FolderItem exposing (FolderItem)
 import Api.Model.FolderList exposing (FolderList)
@@ -96,6 +97,7 @@ type alias Model =
     , keyInputModel : Comp.KeyInput.Model
     , customFieldsModel : Comp.CustomFieldMultiInput.Model
     , customFieldSavingIcon : Dict String String
+    , customFieldThrottle : Throttle Msg
     }
 
 
@@ -199,6 +201,7 @@ emptyModel =
     , keyInputModel = Comp.KeyInput.init
     , customFieldsModel = Comp.CustomFieldMultiInput.initWith []
     , customFieldSavingIcon = Dict.empty
+    , customFieldThrottle = Throttle.create 1
     }
 
 
@@ -285,7 +288,8 @@ type Msg
     | UiSettingsUpdated
     | SetLinkTarget LinkTarget
     | CustomFieldMsg Comp.CustomFieldMultiInput.Msg
-    | CustomFieldSaveResp String (Result Http.Error BasicResult)
+    | CustomFieldSaveResp CustomField String (Result Http.Error BasicResult)
+    | CustomFieldRemoveResp String (Result Http.Error BasicResult)
 
 
 type SaveNameState
diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm
index 2dd84c8d..7ea31fdc 100644
--- a/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm
+++ b/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm
@@ -2,10 +2,12 @@ module Comp.ItemDetail.Update exposing (update)
 
 import Api
 import Api.Model.BasicResult exposing (BasicResult)
+import Api.Model.CustomField exposing (CustomField)
 import Api.Model.CustomFieldValue exposing (CustomFieldValue)
 import Api.Model.DirectionValue exposing (DirectionValue)
 import Api.Model.IdName exposing (IdName)
 import Api.Model.ItemDetail exposing (ItemDetail)
+import Api.Model.ItemFieldValue exposing (ItemFieldValue)
 import Api.Model.MoveAttachment exposing (MoveAttachment)
 import Api.Model.OptionalDate exposing (OptionalDate)
 import Api.Model.OptionalId exposing (OptionalId)
@@ -1232,10 +1234,16 @@ update key flags inav settings msg model =
 
         UpdateThrottle ->
             let
-                ( newThrottle, cmd ) =
+                ( newSaveName, cmd1 ) =
                     Throttle.update model.nameSaveThrottle
+
+                ( newCustomField, cmd2 ) =
+                    Throttle.update model.customFieldThrottle
             in
-            withSub ( { model | nameSaveThrottle = newThrottle }, cmd )
+            withSub
+                ( { model | nameSaveThrottle = newSaveName, customFieldThrottle = newCustomField }
+                , Cmd.batch [ cmd1, cmd2 ]
+                )
 
         KeyInputMsg lm ->
             let
@@ -1303,7 +1311,7 @@ update key flags inav settings msg model =
                 loadingIcon =
                     "refresh loading icon"
 
-                ( action, icons ) =
+                ( action_, icons ) =
                     case result.result of
                         NoFieldChange ->
                             ( Cmd.none, model.customFieldSavingIcon )
@@ -1312,7 +1320,7 @@ update key flags inav settings msg model =
                             ( Api.deleteCustomValue flags
                                 model.item.id
                                 field.id
-                                (CustomFieldSaveResp field.id)
+                                (CustomFieldRemoveResp field.id)
                             , Dict.insert field.id loadingIcon model.customFieldSavingIcon
                             )
 
@@ -1320,16 +1328,13 @@ update key flags inav settings msg model =
                             ( Api.putCustomValue flags
                                 model.item.id
                                 (CustomFieldValue field.id value)
-                                (CustomFieldSaveResp field.id)
+                                (CustomFieldSaveResp field value)
                             , Dict.insert field.id loadingIcon model.customFieldSavingIcon
                             )
 
                         FieldCreateNew ->
                             ( Cmd.none, model.customFieldSavingIcon )
 
-                sub_ =
-                    Sub.map CustomFieldMsg result.subs
-
                 modalEdit =
                     if result.result == FieldCreateNew then
                         Just (Comp.DetailEdit.initCustomField model.item.id)
@@ -1337,20 +1342,41 @@ update key flags inav settings msg model =
                     else
                         Nothing
 
+                ( throttle, action ) =
+                    if action_ == Cmd.none then
+                        ( model.customFieldThrottle, action_ )
+
+                    else
+                        Throttle.try action_ model.customFieldThrottle
+
                 model_ =
                     { model
                         | customFieldsModel = result.model
+                        , customFieldThrottle = throttle
                         , modalEdit = modalEdit
                         , customFieldSavingIcon = icons
                     }
             in
-            { model = model_
-            , cmd = Cmd.batch [ cmd_, action ]
-            , sub = sub_
-            , linkTarget = Comp.LinkTarget.LinkNone
-            }
+            withSub ( model_, Cmd.batch [ cmd_, action ] )
 
-        CustomFieldSaveResp fieldId (Ok res) ->
+        CustomFieldSaveResp cf fv (Ok res) ->
+            let
+                model_ =
+                    { model | customFieldSavingIcon = Dict.remove cf.id model.customFieldSavingIcon }
+            in
+            if res.success then
+                resultModelCmd
+                    ( { model_ | item = setCustomField model.item cf fv }
+                    , Cmd.none
+                    )
+
+            else
+                resultModel model_
+
+        CustomFieldSaveResp cf _ (Err _) ->
+            resultModel { model | customFieldSavingIcon = Dict.remove cf.id model.customFieldSavingIcon }
+
+        CustomFieldRemoveResp fieldId (Ok res) ->
             let
                 model_ =
                     { model | customFieldSavingIcon = Dict.remove fieldId model.customFieldSavingIcon }
@@ -1364,7 +1390,7 @@ update key flags inav settings msg model =
             else
                 resultModel model_
 
-        CustomFieldSaveResp fieldId (Err _) ->
+        CustomFieldRemoveResp fieldId (Err _) ->
             resultModel { model | customFieldSavingIcon = Dict.remove fieldId model.customFieldSavingIcon }
 
 
@@ -1506,9 +1532,14 @@ withSub ( m, c ) =
     { model = m
     , cmd = c
     , sub =
-        Throttle.ifNeeded
-            (Time.every 400 (\_ -> UpdateThrottle))
-            m.nameSaveThrottle
+        Sub.batch
+            [ Throttle.ifNeeded
+                (Time.every 200 (\_ -> UpdateThrottle))
+                m.nameSaveThrottle
+            , Throttle.ifNeeded
+                (Time.every 200 (\_ -> UpdateThrottle))
+                m.customFieldThrottle
+            ]
     , linkTarget = Comp.LinkTarget.LinkNone
     }
 
@@ -1564,3 +1595,33 @@ resetHiddenFields settings flags item tagger =
 setItemName : ItemDetail -> String -> ItemDetail
 setItemName item name =
     { item | name = name }
+
+
+{-| Sets the field value of the given id into the item detail.
+-}
+setCustomField : ItemDetail -> CustomField -> String -> ItemDetail
+setCustomField item cf fv =
+    let
+        change ifv =
+            if ifv.id == cf.id then
+                ( { ifv | value = fv }, True )
+
+            else
+                ( ifv, False )
+
+        ( fields, isChanged ) =
+            List.map change item.customfields
+                |> List.foldl
+                    (\( e, isChange ) ->
+                        \( list, flag ) -> ( e :: list, isChange || flag )
+                    )
+                    ( [], False )
+    in
+    if isChanged then
+        { item | customfields = fields }
+
+    else
+        { item
+            | customfields =
+                ItemFieldValue cf.id cf.name cf.label cf.ftype fv :: item.customfields
+        }
diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/View.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/View.elm
index b5fbd555..88707470 100644
--- a/modules/webapp/src/main/elm/Comp/ItemDetail/View.elm
+++ b/modules/webapp/src/main/elm/Comp/ItemDetail/View.elm
@@ -183,11 +183,7 @@ renderDetailMenu settings inav model =
 
 actionInputDatePicker : DatePicker.Settings
 actionInputDatePicker =
-    let
-        ds =
-            Comp.DatePicker.defaultSettings
-    in
-    { ds | containerClassList = [ ( "ui action input", True ) ] }
+    Comp.DatePicker.defaultSettings
 
 
 renderIdInfo : Model -> List (Html msg)