diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail.elm b/modules/webapp/src/main/elm/Comp/ItemDetail.elm index 27ba1b60..4009d1ee 100644 --- a/modules/webapp/src/main/elm/Comp/ItemDetail.elm +++ b/modules/webapp/src/main/elm/Comp/ItemDetail.elm @@ -6,8 +6,8 @@ module Comp.ItemDetail exposing ) import Browser.Navigation as Nav -import Comp.ItemDetail.Model -import Comp.ItemDetail.Update exposing (Msg) +import Comp.ItemDetail.Model exposing (Msg(..)) +import Comp.ItemDetail.Update import Comp.ItemDetail.View exposing (..) import Data.Flags exposing (Flags) import Data.UiSettings exposing (UiSettings) diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/Model.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/Model.elm index 1393e707..1aecb71a 100644 --- a/modules/webapp/src/main/elm/Comp/ItemDetail/Model.elm +++ b/modules/webapp/src/main/elm/Comp/ItemDetail/Model.elm @@ -1,17 +1,24 @@ module Comp.ItemDetail.Model exposing ( AttachmentRename , Model + , Msg(..) , NotesField(..) + , SaveNameState(..) , emptyModel , isEditNotes ) import Api.Model.BasicResult exposing (BasicResult) +import Api.Model.EquipmentList exposing (EquipmentList) import Api.Model.FolderItem exposing (FolderItem) +import Api.Model.FolderList exposing (FolderList) import Api.Model.IdName exposing (IdName) import Api.Model.ItemDetail exposing (ItemDetail) import Api.Model.ItemProposals exposing (ItemProposals) +import Api.Model.ReferenceList exposing (ReferenceList) +import Api.Model.SentMails exposing (SentMails) import Api.Model.Tag exposing (Tag) +import Api.Model.TagList exposing (TagList) import Comp.AttachmentMeta import Comp.DatePicker import Comp.DetailEdit @@ -22,14 +29,17 @@ import Comp.MarkdownInput import Comp.SentMails import Comp.YesNoDimmer import Data.Direction exposing (Direction) +import Data.Fields exposing (Field) import DatePicker exposing (DatePicker) import Dict exposing (Dict) import File exposing (File) import Html exposing (..) import Html.Attributes exposing (..) import Html5.DragDrop as DD +import Http import Page exposing (Page(..)) import Set exposing (Set) +import Throttle exposing (Throttle) import Util.Tag @@ -46,6 +56,8 @@ type alias Model = , folderModel : Comp.Dropdown.Model IdName , allFolders : List FolderItem , nameModel : String + , nameState : SaveNameState + , nameSaveThrottle : Throttle Msg , notesModel : Maybe String , notesField : NotesField , deleteItemConfirm : Comp.YesNoDimmer.Model @@ -143,6 +155,8 @@ emptyModel = } , allFolders = [] , nameModel = "" + , nameState = SaveSuccess + , nameSaveThrottle = Throttle.create 1 , notesModel = Nothing , notesField = ViewNotes , deleteItemConfirm = Comp.YesNoDimmer.emptyModel @@ -171,3 +185,89 @@ emptyModel = , modalEdit = Nothing , attachRename = Nothing } + + +type Msg + = ToggleMenu + | ReloadItem + | Init + | SetItem ItemDetail + | SetActiveAttachment Int + | TagDropdownMsg (Comp.Dropdown.Msg Tag) + | DirDropdownMsg (Comp.Dropdown.Msg Direction) + | OrgDropdownMsg (Comp.Dropdown.Msg IdName) + | CorrPersonMsg (Comp.Dropdown.Msg IdName) + | ConcPersonMsg (Comp.Dropdown.Msg IdName) + | ConcEquipMsg (Comp.Dropdown.Msg IdName) + | GetTagsResp (Result Http.Error TagList) + | GetOrgResp (Result Http.Error ReferenceList) + | GetPersonResp (Result Http.Error ReferenceList) + | GetEquipResp (Result Http.Error EquipmentList) + | SetName String + | SetNotes String + | ToggleEditNotes + | NotesEditMsg Comp.MarkdownInput.Msg + | SaveNotes + | ConfirmItem + | UnconfirmItem + | SetCorrOrgSuggestion IdName + | SetCorrPersonSuggestion IdName + | SetConcPersonSuggestion IdName + | SetConcEquipSuggestion IdName + | SetItemDateSuggestion Int + | SetDueDateSuggestion Int + | ItemDatePickerMsg Comp.DatePicker.Msg + | DueDatePickerMsg Comp.DatePicker.Msg + | DeleteItemConfirm Comp.YesNoDimmer.Msg + | RequestDelete + | SaveResp (Result Http.Error BasicResult) + | DeleteResp (Result Http.Error BasicResult) + | GetItemResp (Result Http.Error ItemDetail) + | GetProposalResp (Result Http.Error ItemProposals) + | RemoveDueDate + | RemoveDate + | ItemMailMsg Comp.ItemMail.Msg + | ToggleMail + | SendMailResp (Result Http.Error BasicResult) + | SentMailsMsg Comp.SentMails.Msg + | ToggleSentMails + | SentMailsResp (Result Http.Error SentMails) + | AttachMetaClick String + | AttachMetaMsg String Comp.AttachmentMeta.Msg + | TogglePdfNativeView Bool + | RequestDeleteAttachment String + | DeleteAttachConfirm String Comp.YesNoDimmer.Msg + | DeleteAttachResp (Result Http.Error BasicResult) + | AddFilesToggle + | AddFilesMsg Comp.Dropzone.Msg + | AddFilesSubmitUpload + | AddFilesUploadResp String (Result Http.Error BasicResult) + | AddFilesProgress String Http.Progress + | AddFilesReset + | AttachDDMsg (DD.Msg String String) + | ModalEditMsg Comp.DetailEdit.Msg + | StartTagModal + | StartCorrOrgModal + | StartCorrPersonModal + | StartConcPersonModal + | StartEquipModal + | CloseModal + | EditAttachNameStart String + | EditAttachNameCancel + | EditAttachNameSet String + | EditAttachNameSubmit + | EditAttachNameResp (Result Http.Error BasicResult) + | GetFolderResp (Result Http.Error FolderList) + | FolderDropdownMsg (Comp.Dropdown.Msg IdName) + | StartEditCorrOrgModal + | StartEditPersonModal (Comp.Dropdown.Model IdName) + | StartEditEquipModal + | ResetHiddenMsg Field (Result Http.Error BasicResult) + | SaveNameResp (Result Http.Error BasicResult) + | UpdateThrottle + + +type SaveNameState + = Saving + | SaveSuccess + | SaveFailed diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm index 22b3d0c2..d768b0e2 100644 --- a/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm +++ b/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm @@ -1,21 +1,16 @@ -module Comp.ItemDetail.Update exposing (Msg(..), update) +module Comp.ItemDetail.Update exposing (update) import Api import Api.Model.BasicResult exposing (BasicResult) import Api.Model.DirectionValue exposing (DirectionValue) -import Api.Model.EquipmentList exposing (EquipmentList) -import Api.Model.FolderList exposing (FolderList) import Api.Model.IdName exposing (IdName) import Api.Model.ItemDetail exposing (ItemDetail) -import Api.Model.ItemProposals exposing (ItemProposals) import Api.Model.MoveAttachment exposing (MoveAttachment) import Api.Model.OptionalDate exposing (OptionalDate) import Api.Model.OptionalId exposing (OptionalId) import Api.Model.OptionalText exposing (OptionalText) import Api.Model.ReferenceList exposing (ReferenceList) -import Api.Model.SentMails exposing (SentMails) import Api.Model.Tag exposing (Tag) -import Api.Model.TagList exposing (TagList) import Browser.Navigation as Nav import Comp.AttachmentMeta import Comp.DatePicker @@ -23,7 +18,15 @@ import Comp.DetailEdit import Comp.Dropdown exposing (isDropdownChangeMsg) import Comp.Dropzone import Comp.EquipmentForm -import Comp.ItemDetail.Model exposing (AttachmentRename, Model, NotesField(..), isEditNotes) +import Comp.ItemDetail.Model + exposing + ( AttachmentRename + , Model + , Msg(..) + , NotesField(..) + , SaveNameState(..) + , isEditNotes + ) import Comp.ItemMail import Comp.MarkdownInput import Comp.OrgForm @@ -43,6 +46,8 @@ import Http import Page exposing (Page(..)) import Ports import Set exposing (Set) +import Throttle +import Time import Util.File exposing (makeFileId) import Util.Folder exposing (mkFolderOption) import Util.Http @@ -51,85 +56,6 @@ import Util.Maybe import Util.String -type Msg - = ToggleMenu - | ReloadItem - | Init - | SetItem ItemDetail - | SetActiveAttachment Int - | TagDropdownMsg (Comp.Dropdown.Msg Tag) - | DirDropdownMsg (Comp.Dropdown.Msg Direction) - | OrgDropdownMsg (Comp.Dropdown.Msg IdName) - | CorrPersonMsg (Comp.Dropdown.Msg IdName) - | ConcPersonMsg (Comp.Dropdown.Msg IdName) - | ConcEquipMsg (Comp.Dropdown.Msg IdName) - | GetTagsResp (Result Http.Error TagList) - | GetOrgResp (Result Http.Error ReferenceList) - | GetPersonResp (Result Http.Error ReferenceList) - | GetEquipResp (Result Http.Error EquipmentList) - | SetName String - | SaveName - | SetNotes String - | ToggleEditNotes - | NotesEditMsg Comp.MarkdownInput.Msg - | SaveNotes - | ConfirmItem - | UnconfirmItem - | SetCorrOrgSuggestion IdName - | SetCorrPersonSuggestion IdName - | SetConcPersonSuggestion IdName - | SetConcEquipSuggestion IdName - | SetItemDateSuggestion Int - | SetDueDateSuggestion Int - | ItemDatePickerMsg Comp.DatePicker.Msg - | DueDatePickerMsg Comp.DatePicker.Msg - | DeleteItemConfirm Comp.YesNoDimmer.Msg - | RequestDelete - | SaveResp (Result Http.Error BasicResult) - | DeleteResp (Result Http.Error BasicResult) - | GetItemResp (Result Http.Error ItemDetail) - | GetProposalResp (Result Http.Error ItemProposals) - | RemoveDueDate - | RemoveDate - | ItemMailMsg Comp.ItemMail.Msg - | ToggleMail - | SendMailResp (Result Http.Error BasicResult) - | SentMailsMsg Comp.SentMails.Msg - | ToggleSentMails - | SentMailsResp (Result Http.Error SentMails) - | AttachMetaClick String - | AttachMetaMsg String Comp.AttachmentMeta.Msg - | TogglePdfNativeView Bool - | RequestDeleteAttachment String - | DeleteAttachConfirm String Comp.YesNoDimmer.Msg - | DeleteAttachResp (Result Http.Error BasicResult) - | AddFilesToggle - | AddFilesMsg Comp.Dropzone.Msg - | AddFilesSubmitUpload - | AddFilesUploadResp String (Result Http.Error BasicResult) - | AddFilesProgress String Http.Progress - | AddFilesReset - | AttachDDMsg (DD.Msg String String) - | ModalEditMsg Comp.DetailEdit.Msg - | StartTagModal - | StartCorrOrgModal - | StartCorrPersonModal - | StartConcPersonModal - | StartEquipModal - | CloseModal - | EditAttachNameStart String - | EditAttachNameCancel - | EditAttachNameSet String - | EditAttachNameSubmit - | EditAttachNameResp (Result Http.Error BasicResult) - | GetFolderResp (Result Http.Error FolderList) - | FolderDropdownMsg (Comp.Dropdown.Msg IdName) - | StartEditCorrOrgModal - | StartEditPersonModal (Comp.Dropdown.Model IdName) - | StartEditEquipModal - | ResetHiddenMsg Field (Result Http.Error BasicResult) - - update : Nav.Key -> Flags -> Maybe String -> UiSettings -> Msg -> Model -> ( Model, Cmd Msg, Sub Msg ) update key flags next settings msg model = case msg of @@ -265,6 +191,7 @@ update key flags next settings msg model = ( { m8 | item = item , nameModel = item.name + , nameState = SaveSuccess , notesModel = item.notes , notesField = if Util.String.isNothingOrBlank item.notes then @@ -448,10 +375,28 @@ update key flags next settings msg model = noSub ( newModel, Cmd.batch [ save, Cmd.map ConcEquipMsg c2 ] ) SetName str -> - noSub ( { model | nameModel = str }, Cmd.none ) + case Util.Maybe.fromString str of + Just newName -> + let + nm = + { model | nameModel = newName } - SaveName -> - noSub ( model, setName flags model ) + cmd_ = + setName flags nm + + ( newThrottle, cmd ) = + Throttle.try cmd_ nm.nameSaveThrottle + in + withSub + ( { nm + | nameState = Saving + , nameSaveThrottle = newThrottle + } + , cmd + ) + + Nothing -> + noSub ( { model | nameModel = str, nameState = SaveFailed }, Cmd.none ) SetNotes str -> noSub @@ -659,6 +604,25 @@ update key flags next settings msg model = SaveResp (Err _) -> noSub ( model, Cmd.none ) + SaveNameResp (Ok res) -> + if res.success then + noSub + ( { model + | nameState = SaveSuccess + , item = setItemName model.item model.nameModel + } + , Cmd.none + ) + + else + noSub + ( { model | nameState = SaveFailed } + , Cmd.none + ) + + SaveNameResp (Err _) -> + noSub ( { model | nameState = SaveFailed }, Cmd.none ) + DeleteResp (Ok res) -> if res.success then case next of @@ -1257,6 +1221,13 @@ update key flags next settings msg model = ResetHiddenMsg _ _ -> noSub ( model, Cmd.none ) + UpdateThrottle -> + let + ( newThrottle, cmd ) = + Throttle.update model.nameSaveThrottle + in + withSub ( { model | nameSaveThrottle = newThrottle }, cmd ) + --- Helper @@ -1359,7 +1330,7 @@ setName flags model = Cmd.none else - Api.setItemName flags model.item.id text SaveResp + Api.setItemName flags model.item.id text SaveNameResp setNotes : Flags -> Model -> Cmd Msg @@ -1396,6 +1367,16 @@ noSub ( m, c ) = ( m, c, Sub.none ) +withSub : ( Model, Cmd Msg ) -> ( Model, Cmd Msg, Sub Msg ) +withSub ( m, c ) = + ( m + , c + , Throttle.ifNeeded + (Time.every 400 (\_ -> UpdateThrottle)) + m.nameSaveThrottle + ) + + resetField : Flags -> String -> (Field -> Result Http.Error BasicResult -> msg) -> Field -> Cmd msg resetField flags item tagger field = case field of @@ -1436,3 +1417,8 @@ resetHiddenFields : resetHiddenFields settings flags item tagger = List.filter (Data.UiSettings.fieldHidden settings) Data.Fields.all |> List.map (resetField flags item tagger) + + +setItemName : ItemDetail -> String -> ItemDetail +setItemName item name = + { item | name = name } diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/View.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/View.elm index 396507d8..216a5832 100644 --- a/modules/webapp/src/main/elm/Comp/ItemDetail/View.elm +++ b/modules/webapp/src/main/elm/Comp/ItemDetail/View.elm @@ -6,14 +6,13 @@ import Comp.DatePicker import Comp.DetailEdit import Comp.Dropdown import Comp.Dropzone -import Comp.ItemDetail.Model exposing (Model, NotesField(..)) -import Comp.ItemDetail.Update exposing (Msg(..)) +import Comp.ItemDetail.Model exposing (Model, Msg(..), NotesField(..), SaveNameState(..)) import Comp.ItemMail import Comp.MarkdownInput import Comp.SentMails import Comp.YesNoDimmer import Data.Direction -import Data.Fields exposing (Field) +import Data.Fields import Data.Icons as Icons import Data.UiSettings exposing (UiSettings) import DatePicker @@ -801,14 +800,16 @@ renderEditForm settings model = ] , div [ class " field" ] [ label [] [ text "Name" ] - , div [ class "ui action input" ] + , div [ class "ui icon input" ] [ input [ type_ "text", value model.nameModel, onInput SetName ] [] - , button - [ class "ui icon button" - , onClick SaveName - ] - [ i [ class "save outline icon" ] [] + , i + [ classList + [ ( "green check icon", model.nameState == SaveSuccess ) + , ( "red exclamation triangle icon", model.nameState == SaveFailed ) + , ( "sync loading icon", model.nameState == Saving ) + ] ] + [] ] ] , optional [ Data.Fields.Folder ] <| diff --git a/modules/webapp/src/main/elm/Page/ItemDetail/Data.elm b/modules/webapp/src/main/elm/Page/ItemDetail/Data.elm index 8255166f..ed432e9f 100644 --- a/modules/webapp/src/main/elm/Page/ItemDetail/Data.elm +++ b/modules/webapp/src/main/elm/Page/ItemDetail/Data.elm @@ -3,7 +3,7 @@ module Page.ItemDetail.Data exposing (Model, Msg(..), emptyModel) import Api.Model.ItemDetail exposing (ItemDetail) import Browser.Dom as Dom import Comp.ItemDetail -import Comp.ItemDetail.Update +import Comp.ItemDetail.Model import Http @@ -20,6 +20,6 @@ emptyModel = type Msg = Init String - | ItemDetailMsg Comp.ItemDetail.Update.Msg + | ItemDetailMsg Comp.ItemDetail.Model.Msg | ItemResp (Result Http.Error ItemDetail) | ScrollResult (Result Dom.Error ()) diff --git a/modules/webapp/src/main/elm/Page/ItemDetail/Update.elm b/modules/webapp/src/main/elm/Page/ItemDetail/Update.elm index b1a6abe9..f852c33c 100644 --- a/modules/webapp/src/main/elm/Page/ItemDetail/Update.elm +++ b/modules/webapp/src/main/elm/Page/ItemDetail/Update.elm @@ -3,7 +3,7 @@ module Page.ItemDetail.Update exposing (update) import Api import Browser.Navigation as Nav import Comp.ItemDetail -import Comp.ItemDetail.Update +import Comp.ItemDetail.Model import Data.Flags exposing (Flags) import Data.UiSettings exposing (UiSettings) import Page.ItemDetail.Data exposing (Model, Msg(..)) @@ -21,7 +21,7 @@ update key flags next settings msg model = flags next settings - Comp.ItemDetail.Update.Init + Comp.ItemDetail.Model.Init model.detail task = @@ -49,7 +49,7 @@ update key flags next settings msg model = ItemResp (Ok item) -> let lmsg = - Comp.ItemDetail.Update.SetItem item + Comp.ItemDetail.Model.SetItem item in update key flags next settings (ItemDetailMsg lmsg) model