docspell/modules/webapp/src/main/elm/Comp/NotificationChannelManage.elm
eikek 23cb34a6ff Manage notification channels separately and migrate
It's more convenient to manage notification channels separately, as it
is done with email settings. Notification hook and other forms are
adopted to only select channels. Hooks can now use more than one
channel.
2022-01-19 21:57:30 +01:00

455 lines
13 KiB
Elm

{-
Copyright 2020 Eike K. & Contributors
SPDX-License-Identifier: AGPL-3.0-or-later
-}
module Comp.NotificationChannelManage exposing
( Model
, Msg
, init
, update
, view
)
import Api
import Api.Model.BasicResult exposing (BasicResult)
import Comp.Basic as B
import Comp.ChannelForm
import Comp.ChannelMenu
import Comp.MenuBar as MB
import Comp.NotificationChannelTable
import Data.ChannelType exposing (ChannelType)
import Data.Flags exposing (Flags)
import Data.NotificationChannel exposing (NotificationChannel)
import Data.UiSettings exposing (UiSettings)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Http
import Messages.Comp.NotificationChannelManage exposing (Texts)
import Styles as S
type alias Model =
{ listModel : Comp.NotificationChannelTable.Model
, detailModel : Maybe Comp.ChannelForm.Model
, items : List NotificationChannel
, deleteConfirm : DeleteConfirm
, loading : Bool
, formState : FormState
, newChannelMenuOpen : Bool
, jsonFilterError : Maybe String
}
type DeleteConfirm
= DeleteConfirmOff
| DeleteConfirmOn
type SubmitType
= SubmitDelete
| SubmitUpdate
| SubmitCreate
type FormState
= FormStateInitial
| FormErrorHttp Http.Error
| FormSubmitSuccessful SubmitType
| FormErrorSubmit String
| FormErrorInvalid
type Msg
= TableMsg Comp.NotificationChannelTable.Msg
| DetailMsg Comp.ChannelForm.Msg
| GetDataResp (Result Http.Error (List NotificationChannel))
| ToggleNewChannelMenu
| SubmitResp SubmitType (Result Http.Error BasicResult)
| NewChannelInit ChannelType
| BackToTable
| Submit
| RequestDelete
| CancelDelete
| DeleteChannelNow String
initModel : Model
initModel =
{ listModel = Comp.NotificationChannelTable.init
, detailModel = Nothing
, items = []
, loading = False
, formState = FormStateInitial
, newChannelMenuOpen = False
, deleteConfirm = DeleteConfirmOff
, jsonFilterError = Nothing
}
initCmd : Flags -> Cmd Msg
initCmd flags =
Api.getChannels flags GetDataResp
init : Flags -> ( Model, Cmd Msg )
init flags =
( initModel, initCmd flags )
--- Update
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
update flags msg model =
case msg of
GetDataResp (Ok res) ->
( { model
| items = res
, formState = FormStateInitial
}
, Cmd.none
)
GetDataResp (Err err) ->
( { model | formState = FormErrorHttp err }
, Cmd.none
)
TableMsg lm ->
let
( mm, action ) =
Comp.NotificationChannelTable.update flags lm model.listModel
( detail, cmd ) =
case action of
Comp.NotificationChannelTable.NoAction ->
( Nothing, Cmd.none )
Comp.NotificationChannelTable.EditAction channel ->
let
( dm, dc ) =
Comp.ChannelForm.initWith flags channel
in
( Just dm, Cmd.map DetailMsg dc )
in
( { model
| listModel = mm
, detailModel = detail
}
, cmd
)
DetailMsg lm ->
case model.detailModel of
Just dm ->
let
( mm, mc ) =
Comp.ChannelForm.update flags lm dm
in
( { model | detailModel = Just mm }
, Cmd.map DetailMsg mc
)
Nothing ->
( model, Cmd.none )
ToggleNewChannelMenu ->
( { model | newChannelMenuOpen = not model.newChannelMenuOpen }, Cmd.none )
SubmitResp submitType (Ok res) ->
( { model
| formState =
if res.success then
FormSubmitSuccessful submitType
else
FormErrorSubmit res.message
, detailModel =
if submitType == SubmitDelete then
Nothing
else
model.detailModel
, loading = False
}
, if submitType == SubmitDelete then
initCmd flags
else
Cmd.none
)
SubmitResp _ (Err err) ->
( { model | formState = FormErrorHttp err, loading = False }
, Cmd.none
)
NewChannelInit ct ->
let
( mm, mc ) =
Comp.ChannelForm.init flags ct
in
( { model | detailModel = Just mm, newChannelMenuOpen = False }, Cmd.map DetailMsg mc )
BackToTable ->
( { model | detailModel = Nothing }, initCmd flags )
Submit ->
case model.detailModel of
Just dm ->
case Comp.ChannelForm.getChannel dm of
Just data ->
postChannel flags data model
Nothing ->
( { model | formState = FormErrorInvalid }, Cmd.none )
Nothing ->
( model, Cmd.none )
RequestDelete ->
( { model | deleteConfirm = DeleteConfirmOn }, Cmd.none )
CancelDelete ->
( { model | deleteConfirm = DeleteConfirmOff }, Cmd.none )
DeleteChannelNow id ->
( { model | deleteConfirm = DeleteConfirmOff, loading = True }
, Api.deleteChannel flags id (SubmitResp SubmitDelete)
)
postChannel : Flags -> NotificationChannel -> Model -> ( Model, Cmd Msg )
postChannel flags channel model =
if (Data.NotificationChannel.getRef channel |> .id) == "" then
( { model | loading = True }, Api.createChannel flags channel (SubmitResp SubmitCreate) )
else
( { model | loading = True }, Api.updateChannel flags channel (SubmitResp SubmitUpdate) )
--- View2
view : Texts -> UiSettings -> Model -> Html Msg
view texts settings model =
div [ class "flex flex-col" ]
(case model.detailModel of
Just msett ->
viewForm texts settings model msett
Nothing ->
viewList texts model
)
viewState : Texts -> Model -> Html Msg
viewState texts model =
div
[ classList
[ ( S.errorMessage, not (isSuccess model.formState) )
, ( S.successMessage, isSuccess model.formState )
, ( "hidden", model.formState == FormStateInitial )
]
, class "mb-2"
]
[ case model.formState of
FormStateInitial ->
text ""
FormSubmitSuccessful SubmitCreate ->
text texts.channelCreated
FormSubmitSuccessful SubmitUpdate ->
text texts.channelUpdated
FormSubmitSuccessful SubmitDelete ->
text texts.channelDeleted
FormErrorSubmit m ->
text m
FormErrorHttp err ->
text (texts.httpError err)
FormErrorInvalid ->
text texts.formInvalid
]
isSuccess : FormState -> Bool
isSuccess state =
case state of
FormSubmitSuccessful _ ->
True
_ ->
False
viewForm : Texts -> UiSettings -> Model -> Comp.ChannelForm.Model -> List (Html Msg)
viewForm texts settings outerModel model =
let
channelId =
Comp.ChannelForm.getChannel model
|> Maybe.map Data.NotificationChannel.getRef
|> Maybe.map .id
newChannel =
channelId |> (==) (Just "")
headline =
case Comp.ChannelForm.channelType model of
Data.ChannelType.Matrix ->
span []
[ text texts.integrate
, a
[ href "https://matrix.org"
, target "_blank"
, class S.link
, class "mx-3"
]
[ i [ class "fa fa-external-link-alt mr-1" ] []
, text "Matrix"
]
, text texts.intoDocspell
]
Data.ChannelType.Mail ->
span []
[ text texts.notifyEmailInfo
]
Data.ChannelType.Gotify ->
span []
[ text texts.integrate
, a
[ href "https://gotify.net"
, target "_blank"
, class S.link
, class "mx-3"
]
[ i [ class "fa fa-external-link-alt mr-1" ] []
, text "Gotify"
]
, text texts.intoDocspell
]
Data.ChannelType.Http ->
span []
[ text texts.postRequestInfo
]
in
[ h1 [ class S.header2 ]
[ Data.ChannelType.icon (Comp.ChannelForm.channelType model) "w-8 h-8 inline-block mr-2"
, if newChannel then
text texts.addChannel
else
text texts.updateChannel
, div [ class "text-xs opacity-50 font-mono" ]
[ Maybe.withDefault "" channelId |> text
]
]
, div [ class "pt-2 pb-4 font-medium" ]
[ headline
]
, MB.view
{ start =
[ MB.CustomElement <|
B.primaryButton
{ handler = onClick Submit
, title = texts.basics.submitThisForm
, icon = "fa fa-save"
, label = texts.basics.submit
, disabled = False
, attrs = [ href "#" ]
}
, MB.SecondaryButton
{ tagger = BackToTable
, title = texts.basics.backToList
, icon = Just "fa fa-arrow-left"
, label = texts.basics.backToList
}
]
, end =
if not newChannel then
[ MB.DeleteButton
{ tagger = RequestDelete
, title = texts.deleteThisChannel
, icon = Just "fa fa-trash"
, label = texts.basics.delete
}
]
else
[]
, rootClasses = "mb-4"
}
, div [ class "mt-2" ]
[ viewState texts outerModel
]
, Html.map DetailMsg
(Comp.ChannelForm.view texts.notificationForm settings model)
, B.loadingDimmer
{ active = outerModel.loading
, label = texts.basics.loading
}
, B.contentDimmer
(outerModel.deleteConfirm == DeleteConfirmOn)
(div [ class "flex flex-col" ]
[ div [ class "text-lg" ]
[ i [ class "fa fa-info-circle mr-2" ] []
, text texts.reallyDeleteChannel
]
, div [ class "mt-4 flex flex-row items-center" ]
[ B.deleteButton
{ label = texts.basics.yes
, icon = "fa fa-check"
, disabled = False
, handler = onClick (DeleteChannelNow (Maybe.withDefault "" channelId))
, attrs = [ href "#" ]
}
, B.secondaryButton
{ label = texts.basics.no
, icon = "fa fa-times"
, disabled = False
, handler = onClick CancelDelete
, attrs = [ href "#", class "ml-2" ]
}
]
]
)
]
viewList : Texts -> Model -> List (Html Msg)
viewList texts model =
let
menuModel =
{ menuOpen = model.newChannelMenuOpen
, toggleMenu = ToggleNewChannelMenu
, menuLabel = texts.newChannel
, onItem = NewChannelInit
}
in
[ MB.view
{ start = []
, end =
[ Comp.ChannelMenu.channelMenu texts.channelType menuModel
]
, rootClasses = "mb-4"
}
, Html.map TableMsg
(Comp.NotificationChannelTable.view texts.notificationTable
model.listModel
model.items
)
]