mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-07 07:35:59 +00:00
614 lines
18 KiB
Elm
614 lines
18 KiB
Elm
module Comp.ScanMailboxForm exposing
|
|
( Action(..)
|
|
, Model
|
|
, Msg
|
|
, init
|
|
, initWith
|
|
, update
|
|
, view
|
|
)
|
|
|
|
import Api
|
|
import Api.Model.BasicResult exposing (BasicResult)
|
|
import Api.Model.ImapSettingsList exposing (ImapSettingsList)
|
|
import Api.Model.ScanMailboxSettings exposing (ScanMailboxSettings)
|
|
import Comp.CalEventInput
|
|
import Comp.Dropdown
|
|
import Comp.IntField
|
|
import Comp.StringListInput
|
|
import Comp.YesNoDimmer
|
|
import Data.CalEvent exposing (CalEvent)
|
|
import Data.Direction exposing (Direction(..))
|
|
import Data.Flags exposing (Flags)
|
|
import Data.Validated exposing (Validated(..))
|
|
import Html exposing (..)
|
|
import Html.Attributes exposing (..)
|
|
import Html.Events exposing (onCheck, onClick, onInput)
|
|
import Http
|
|
import Util.Http
|
|
import Util.List
|
|
import Util.Maybe
|
|
|
|
|
|
type alias Model =
|
|
{ settings : ScanMailboxSettings
|
|
, connectionModel : Comp.Dropdown.Model String
|
|
, enabled : Bool
|
|
, deleteMail : Bool
|
|
, receivedHours : Maybe Int
|
|
, receivedHoursModel : Comp.IntField.Model
|
|
, targetFolder : Maybe String
|
|
, foldersModel : Comp.StringListInput.Model
|
|
, folders : List String
|
|
, direction : Maybe Direction
|
|
, schedule : Validated CalEvent
|
|
, scheduleModel : Comp.CalEventInput.Model
|
|
, formMsg : Maybe BasicResult
|
|
, loading : Int
|
|
, yesNoDelete : Comp.YesNoDimmer.Model
|
|
}
|
|
|
|
|
|
type Action
|
|
= SubmitAction ScanMailboxSettings
|
|
| StartOnceAction ScanMailboxSettings
|
|
| CancelAction
|
|
| DeleteAction String
|
|
| NoAction
|
|
|
|
|
|
type Msg
|
|
= Submit
|
|
| Cancel
|
|
| RequestDelete
|
|
| ConnMsg (Comp.Dropdown.Msg String)
|
|
| ConnResp (Result Http.Error ImapSettingsList)
|
|
| ToggleEnabled
|
|
| ToggleDeleteMail
|
|
| CalEventMsg Comp.CalEventInput.Msg
|
|
| StartOnce
|
|
| ReceivedHoursMsg Comp.IntField.Msg
|
|
| SetTargetFolder String
|
|
| FoldersMsg Comp.StringListInput.Msg
|
|
| DirectionMsg (Maybe Direction)
|
|
| YesNoDeleteMsg Comp.YesNoDimmer.Msg
|
|
|
|
|
|
initWith : Flags -> ScanMailboxSettings -> ( Model, Cmd Msg )
|
|
initWith flags s =
|
|
let
|
|
( im, _ ) =
|
|
init flags
|
|
|
|
imap =
|
|
Util.Maybe.fromString s.imapConnection
|
|
|> Maybe.map List.singleton
|
|
|> Maybe.withDefault []
|
|
|
|
( nm, _, nc ) =
|
|
update flags (ConnMsg (Comp.Dropdown.SetSelection imap)) im
|
|
|
|
newSchedule =
|
|
Data.CalEvent.fromEvent s.schedule
|
|
|> Maybe.withDefault Data.CalEvent.everyMonth
|
|
|
|
( sm, sc ) =
|
|
Comp.CalEventInput.init flags newSchedule
|
|
in
|
|
( { nm
|
|
| settings = s
|
|
, enabled = s.enabled
|
|
, deleteMail = s.deleteMail
|
|
, receivedHours = s.receivedSinceHours
|
|
, targetFolder = s.targetFolder
|
|
, folders = s.folders
|
|
, schedule = Data.Validated.Unknown newSchedule
|
|
, direction = Maybe.andThen Data.Direction.fromString s.direction
|
|
, scheduleModel = sm
|
|
, formMsg = Nothing
|
|
, yesNoDelete = Comp.YesNoDimmer.emptyModel
|
|
}
|
|
, Cmd.batch
|
|
[ Api.getImapSettings flags "" ConnResp
|
|
, nc
|
|
, Cmd.map CalEventMsg sc
|
|
]
|
|
)
|
|
|
|
|
|
init : Flags -> ( Model, Cmd Msg )
|
|
init flags =
|
|
let
|
|
initialSchedule =
|
|
Data.Validated.Unknown Data.CalEvent.everyMonth
|
|
|
|
( sm, sc ) =
|
|
Comp.CalEventInput.init flags Data.CalEvent.everyMonth
|
|
in
|
|
( { settings = Api.Model.ScanMailboxSettings.empty
|
|
, connectionModel =
|
|
Comp.Dropdown.makeSingle
|
|
{ makeOption = \a -> { value = a, text = a }
|
|
, placeholder = "Select connection..."
|
|
}
|
|
, enabled = False
|
|
, deleteMail = False
|
|
, receivedHours = Nothing
|
|
, receivedHoursModel = Comp.IntField.init (Just 1) Nothing True "Received Since Hours"
|
|
, foldersModel = Comp.StringListInput.init
|
|
, folders = []
|
|
, targetFolder = Nothing
|
|
, direction = Nothing
|
|
, schedule = initialSchedule
|
|
, scheduleModel = sm
|
|
, formMsg = Nothing
|
|
, loading = 1
|
|
, yesNoDelete = Comp.YesNoDimmer.emptyModel
|
|
}
|
|
, Cmd.batch
|
|
[ Api.getImapSettings flags "" ConnResp
|
|
, Cmd.map CalEventMsg sc
|
|
]
|
|
)
|
|
|
|
|
|
|
|
--- Update
|
|
|
|
|
|
makeSettings : Model -> Validated ScanMailboxSettings
|
|
makeSettings model =
|
|
let
|
|
prev =
|
|
model.settings
|
|
|
|
conn =
|
|
Comp.Dropdown.getSelected model.connectionModel
|
|
|> List.head
|
|
|> Maybe.map Valid
|
|
|> Maybe.withDefault (Invalid [ "Connection missing" ] "")
|
|
|
|
infolders =
|
|
if model.folders == [] then
|
|
Invalid [ "No folders given" ] []
|
|
|
|
else
|
|
Valid model.folders
|
|
|
|
make smtp timer folders =
|
|
{ prev
|
|
| imapConnection = smtp
|
|
, enabled = model.enabled
|
|
, receivedSinceHours = model.receivedHours
|
|
, deleteMail = model.deleteMail
|
|
, targetFolder = model.targetFolder
|
|
, folders = folders
|
|
, direction = Maybe.map Data.Direction.toString model.direction
|
|
, schedule = Data.CalEvent.makeEvent timer
|
|
}
|
|
in
|
|
Data.Validated.map3 make
|
|
conn
|
|
model.schedule
|
|
infolders
|
|
|
|
|
|
withValidSettings : (ScanMailboxSettings -> Action) -> Model -> ( Model, Action, Cmd Msg )
|
|
withValidSettings mkAction model =
|
|
case makeSettings model of
|
|
Valid set ->
|
|
( { model | formMsg = Nothing }
|
|
, mkAction set
|
|
, Cmd.none
|
|
)
|
|
|
|
Invalid errs _ ->
|
|
let
|
|
errMsg =
|
|
String.join ", " errs
|
|
in
|
|
( { model | formMsg = Just (BasicResult False errMsg) }
|
|
, NoAction
|
|
, Cmd.none
|
|
)
|
|
|
|
Unknown _ ->
|
|
( { model | formMsg = Just (BasicResult False "An unknown error occured") }
|
|
, NoAction
|
|
, Cmd.none
|
|
)
|
|
|
|
|
|
update : Flags -> Msg -> Model -> ( Model, Action, Cmd Msg )
|
|
update flags msg model =
|
|
case msg of
|
|
CalEventMsg lmsg ->
|
|
let
|
|
( cm, cc, cs ) =
|
|
Comp.CalEventInput.update flags
|
|
(Data.Validated.value model.schedule)
|
|
lmsg
|
|
model.scheduleModel
|
|
in
|
|
( { model
|
|
| schedule = cs
|
|
, scheduleModel = cm
|
|
, formMsg = Nothing
|
|
}
|
|
, NoAction
|
|
, Cmd.map CalEventMsg cc
|
|
)
|
|
|
|
ConnMsg m ->
|
|
let
|
|
( cm, cc ) =
|
|
Comp.Dropdown.update m model.connectionModel
|
|
in
|
|
( { model
|
|
| connectionModel = cm
|
|
, formMsg = Nothing
|
|
}
|
|
, NoAction
|
|
, Cmd.map ConnMsg cc
|
|
)
|
|
|
|
ConnResp (Ok list) ->
|
|
let
|
|
names =
|
|
List.map .name list.items
|
|
|
|
cm =
|
|
Comp.Dropdown.makeSingleList
|
|
{ makeOption = \a -> { value = a, text = a }
|
|
, placeholder = "Select Connection..."
|
|
, options = names
|
|
, selected = List.head names
|
|
}
|
|
in
|
|
( { model
|
|
| connectionModel = cm
|
|
, loading = model.loading - 1
|
|
, formMsg =
|
|
if names == [] then
|
|
Just
|
|
(BasicResult False
|
|
"No E-Mail connections configured. Goto E-Mail Settings to add one."
|
|
)
|
|
|
|
else
|
|
Nothing
|
|
}
|
|
, NoAction
|
|
, Cmd.none
|
|
)
|
|
|
|
ConnResp (Err err) ->
|
|
( { model
|
|
| formMsg = Just (BasicResult False (Util.Http.errorToString err))
|
|
, loading = model.loading - 1
|
|
}
|
|
, NoAction
|
|
, Cmd.none
|
|
)
|
|
|
|
ToggleEnabled ->
|
|
( { model
|
|
| enabled = not model.enabled
|
|
, formMsg = Nothing
|
|
}
|
|
, NoAction
|
|
, Cmd.none
|
|
)
|
|
|
|
ToggleDeleteMail ->
|
|
( { model
|
|
| deleteMail = not model.deleteMail
|
|
, formMsg = Nothing
|
|
}
|
|
, NoAction
|
|
, Cmd.none
|
|
)
|
|
|
|
ReceivedHoursMsg m ->
|
|
let
|
|
( pm, val ) =
|
|
Comp.IntField.update m model.receivedHoursModel
|
|
in
|
|
( { model
|
|
| receivedHoursModel = pm
|
|
, receivedHours = val
|
|
, formMsg = Nothing
|
|
}
|
|
, NoAction
|
|
, Cmd.none
|
|
)
|
|
|
|
SetTargetFolder str ->
|
|
( { model | targetFolder = Util.Maybe.fromString str }
|
|
, NoAction
|
|
, Cmd.none
|
|
)
|
|
|
|
FoldersMsg lm ->
|
|
let
|
|
( fm, itemAction ) =
|
|
Comp.StringListInput.update lm model.foldersModel
|
|
|
|
newList =
|
|
case itemAction of
|
|
Comp.StringListInput.AddAction s ->
|
|
Util.List.distinct (s :: model.folders)
|
|
|
|
Comp.StringListInput.RemoveAction s ->
|
|
List.filter (\e -> e /= s) model.folders
|
|
|
|
Comp.StringListInput.NoAction ->
|
|
model.folders
|
|
in
|
|
( { model
|
|
| foldersModel = fm
|
|
, folders = newList
|
|
}
|
|
, NoAction
|
|
, Cmd.none
|
|
)
|
|
|
|
DirectionMsg md ->
|
|
( { model | direction = md }
|
|
, NoAction
|
|
, Cmd.none
|
|
)
|
|
|
|
Submit ->
|
|
withValidSettings
|
|
SubmitAction
|
|
model
|
|
|
|
StartOnce ->
|
|
withValidSettings
|
|
StartOnceAction
|
|
model
|
|
|
|
Cancel ->
|
|
( model, CancelAction, Cmd.none )
|
|
|
|
RequestDelete ->
|
|
let
|
|
( ym, _ ) =
|
|
Comp.YesNoDimmer.update
|
|
Comp.YesNoDimmer.activate
|
|
model.yesNoDelete
|
|
in
|
|
( { model | yesNoDelete = ym }
|
|
, NoAction
|
|
, Cmd.none
|
|
)
|
|
|
|
YesNoDeleteMsg lm ->
|
|
let
|
|
( ym, flag ) =
|
|
Comp.YesNoDimmer.update lm model.yesNoDelete
|
|
|
|
act =
|
|
if flag then
|
|
DeleteAction model.settings.id
|
|
|
|
else
|
|
NoAction
|
|
in
|
|
( { model | yesNoDelete = ym }
|
|
, act
|
|
, Cmd.none
|
|
)
|
|
|
|
|
|
|
|
--- View
|
|
|
|
|
|
isFormError : Model -> Bool
|
|
isFormError model =
|
|
Maybe.map .success model.formMsg
|
|
|> Maybe.map not
|
|
|> Maybe.withDefault False
|
|
|
|
|
|
isFormSuccess : Model -> Bool
|
|
isFormSuccess model =
|
|
Maybe.map .success model.formMsg
|
|
|> Maybe.withDefault False
|
|
|
|
|
|
view : String -> Model -> Html Msg
|
|
view extraClasses model =
|
|
div
|
|
[ classList
|
|
[ ( "ui form", True )
|
|
, ( extraClasses, True )
|
|
, ( "error", isFormError model )
|
|
, ( "success", isFormSuccess model )
|
|
]
|
|
]
|
|
[ Html.map YesNoDeleteMsg (Comp.YesNoDimmer.view model.yesNoDelete)
|
|
, div
|
|
[ classList
|
|
[ ( "ui dimmer", True )
|
|
, ( "active", model.loading > 0 )
|
|
]
|
|
]
|
|
[ div [ class "ui text loader" ]
|
|
[ text "Loading..."
|
|
]
|
|
]
|
|
, div [ class "inline field" ]
|
|
[ div [ class "ui checkbox" ]
|
|
[ input
|
|
[ type_ "checkbox"
|
|
, onCheck (\_ -> ToggleEnabled)
|
|
, checked model.enabled
|
|
]
|
|
[]
|
|
, label [] [ text "Enabled" ]
|
|
]
|
|
, span [ class "small-info" ]
|
|
[ text "Enable or disable this task."
|
|
]
|
|
]
|
|
, div [ class "required field" ]
|
|
[ label [] [ text "Mailbox" ]
|
|
, Html.map ConnMsg (Comp.Dropdown.view model.connectionModel)
|
|
, span [ class "small-info" ]
|
|
[ text "The IMAP connection to use when sending notification mails."
|
|
]
|
|
]
|
|
, div [ class "required field" ]
|
|
[ label [] [ text "Folders" ]
|
|
, Html.map FoldersMsg (Comp.StringListInput.view model.folders model.foldersModel)
|
|
, span [ class "small-info" ]
|
|
[ text "The folders to go through"
|
|
]
|
|
]
|
|
, Html.map ReceivedHoursMsg
|
|
(Comp.IntField.viewWithInfo
|
|
"Select mails newer than `now - receivedHours`"
|
|
model.receivedHours
|
|
"field"
|
|
model.receivedHoursModel
|
|
)
|
|
, div [ class "field" ]
|
|
[ label [] [ text "Target folder" ]
|
|
, input
|
|
[ type_ "text"
|
|
, onInput SetTargetFolder
|
|
, Maybe.withDefault "" model.targetFolder |> value
|
|
]
|
|
[]
|
|
, span [ class "small-info" ]
|
|
[ text "Move all mails successfully submitted into this folder."
|
|
]
|
|
]
|
|
, div [ class "inline field" ]
|
|
[ div [ class "ui checkbox" ]
|
|
[ input
|
|
[ type_ "checkbox"
|
|
, onCheck (\_ -> ToggleDeleteMail)
|
|
, checked model.deleteMail
|
|
]
|
|
[]
|
|
, label [] [ text "Delete imported mails" ]
|
|
]
|
|
, span [ class "small-info" ]
|
|
[ text "Whether to delete all mails successfully imported into docspell. This only applies if "
|
|
, em [] [ text "target folder" ]
|
|
, text " is not set."
|
|
]
|
|
]
|
|
, div [ class "required field" ]
|
|
[ label [] [ text "Item direction" ]
|
|
, div [ class "grouped fields" ]
|
|
[ div [ class "field" ]
|
|
[ div [ class "ui radio checkbox" ]
|
|
[ input
|
|
[ type_ "radio"
|
|
, checked (model.direction == Nothing)
|
|
, onCheck (\_ -> DirectionMsg Nothing)
|
|
]
|
|
[]
|
|
, label [] [ text "Automatic" ]
|
|
]
|
|
]
|
|
, div [ class "field" ]
|
|
[ div [ class "ui radio checkbox" ]
|
|
[ input
|
|
[ type_ "radio"
|
|
, checked (model.direction == Just Incoming)
|
|
, onCheck (\_ -> DirectionMsg (Just Incoming))
|
|
]
|
|
[]
|
|
, label [] [ text "Incoming" ]
|
|
]
|
|
]
|
|
, div [ class "field" ]
|
|
[ div [ class "ui radio checkbox" ]
|
|
[ input
|
|
[ type_ "radio"
|
|
, checked (model.direction == Just Outgoing)
|
|
, onCheck (\_ -> DirectionMsg (Just Outgoing))
|
|
]
|
|
[]
|
|
, label [] [ text "Outgoing" ]
|
|
]
|
|
]
|
|
, span [ class "small-info" ]
|
|
[ text "Sets the direction for an item. If you know all mails are incoming or "
|
|
, text "outgoing, you can set it here. Otherwise it will be guessed from looking "
|
|
, text "at sender and receiver."
|
|
]
|
|
]
|
|
]
|
|
, div [ class "required field" ]
|
|
[ label []
|
|
[ text "Schedule"
|
|
, a
|
|
[ class "right-float"
|
|
, href "https://github.com/eikek/calev#what-are-calendar-events"
|
|
, target "_blank"
|
|
]
|
|
[ i [ class "help icon" ] []
|
|
, text "Click here for help"
|
|
]
|
|
]
|
|
, Html.map CalEventMsg
|
|
(Comp.CalEventInput.view ""
|
|
(Data.Validated.value model.schedule)
|
|
model.scheduleModel
|
|
)
|
|
, span [ class "small-info" ]
|
|
[ text "Specify how often and when this task should run. "
|
|
, text "Use English 3-letter weekdays. Either a single value, "
|
|
, text "a list (ex. 1,2,3), a range (ex. 1..3) or a '*' (meaning all) "
|
|
, text "is allowed for each part."
|
|
]
|
|
]
|
|
, div [ class "ui divider" ] []
|
|
, div
|
|
[ classList
|
|
[ ( "ui message", True )
|
|
, ( "success", isFormSuccess model )
|
|
, ( "error", isFormError model )
|
|
, ( "hidden", model.formMsg == Nothing )
|
|
]
|
|
]
|
|
[ Maybe.map .message model.formMsg
|
|
|> Maybe.withDefault ""
|
|
|> text
|
|
]
|
|
, button
|
|
[ class "ui primary button"
|
|
, onClick Submit
|
|
]
|
|
[ text "Submit"
|
|
]
|
|
, button
|
|
[ class "ui secondary button"
|
|
, onClick Cancel
|
|
]
|
|
[ text "Cancel"
|
|
]
|
|
, button
|
|
[ classList
|
|
[ ( "ui red button", True )
|
|
, ( "hidden invisible", model.settings.id == "" )
|
|
]
|
|
, onClick RequestDelete
|
|
]
|
|
[ text "Delete"
|
|
]
|
|
, button
|
|
[ class "ui right floated button"
|
|
, onClick StartOnce
|
|
]
|
|
[ text "Start Once"
|
|
]
|
|
]
|