mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-04-05 10:59:33 +00:00
542 lines
15 KiB
Elm
542 lines
15 KiB
Elm
{-
|
|
Copyright 2020 Eike K. & Contributors
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
-}
|
|
|
|
|
|
module Comp.SourceForm exposing
|
|
( Model
|
|
, Msg(..)
|
|
, getSource
|
|
, init
|
|
, isValid
|
|
, update
|
|
, view2
|
|
)
|
|
|
|
import Api
|
|
import Api.Model.FolderItem exposing (FolderItem)
|
|
import Api.Model.FolderList exposing (FolderList)
|
|
import Api.Model.IdName exposing (IdName)
|
|
import Api.Model.SourceAndTags exposing (SourceAndTags)
|
|
import Api.Model.Tag exposing (Tag)
|
|
import Api.Model.TagList exposing (TagList)
|
|
import Comp.Basic as B
|
|
import Comp.Dropdown exposing (isDropdownChangeMsg)
|
|
import Comp.FixedDropdown
|
|
import Comp.TagDropdown
|
|
import Data.DropdownStyle as DS
|
|
import Data.Flags exposing (Flags)
|
|
import Data.FolderOrder
|
|
import Data.Language exposing (Language)
|
|
import Data.Priority exposing (Priority)
|
|
import Data.UiSettings exposing (UiSettings)
|
|
import Html exposing (..)
|
|
import Html.Attributes exposing (..)
|
|
import Html.Events exposing (onCheck, onInput)
|
|
import Http
|
|
import Markdown
|
|
import Messages.Comp.SourceForm exposing (Texts)
|
|
import Styles as S
|
|
import Util.Folder exposing (mkFolderOption)
|
|
import Util.Maybe
|
|
import Util.Update
|
|
|
|
|
|
type alias Model =
|
|
{ source : SourceAndTags
|
|
, abbrev : String
|
|
, description : Maybe String
|
|
, priorityModel : Comp.FixedDropdown.Model Priority
|
|
, priority : Priority
|
|
, enabled : Bool
|
|
, folderModel : Comp.Dropdown.Model IdName
|
|
, allFolders : List FolderItem
|
|
, folderId : Maybe String
|
|
, tagModel : Comp.TagDropdown.Model
|
|
, fileFilter : Maybe String
|
|
, languageModel : Comp.Dropdown.Model Language
|
|
, language : Maybe String
|
|
, attachmentsOnly : Bool
|
|
}
|
|
|
|
|
|
emptyModel : Model
|
|
emptyModel =
|
|
{ source = Api.Model.SourceAndTags.empty
|
|
, abbrev = ""
|
|
, description = Nothing
|
|
, priorityModel =
|
|
Comp.FixedDropdown.init Data.Priority.all
|
|
, priority = Data.Priority.Low
|
|
, enabled = False
|
|
, folderModel = Comp.Dropdown.makeSingle
|
|
, allFolders = []
|
|
, folderId = Nothing
|
|
, tagModel = Comp.TagDropdown.initWith [] []
|
|
, fileFilter = Nothing
|
|
, languageModel =
|
|
Comp.Dropdown.makeSingleList
|
|
{ options = Data.Language.all
|
|
, selected = Nothing
|
|
}
|
|
, language = Nothing
|
|
, attachmentsOnly = False
|
|
}
|
|
|
|
|
|
init : Flags -> ( Model, Cmd Msg )
|
|
init flags =
|
|
let
|
|
( tm, tc ) =
|
|
Comp.TagDropdown.init flags
|
|
in
|
|
( { emptyModel | tagModel = tm }
|
|
, Cmd.batch
|
|
[ Api.getFolders flags "" Data.FolderOrder.NameAsc False GetFolderResp
|
|
, Cmd.map TagDropdownMsg tc
|
|
]
|
|
)
|
|
|
|
|
|
isValid : Model -> Bool
|
|
isValid model =
|
|
model.abbrev /= ""
|
|
|
|
|
|
getSource : Model -> SourceAndTags
|
|
getSource model =
|
|
let
|
|
st =
|
|
model.source
|
|
|
|
s =
|
|
st.source
|
|
|
|
tags =
|
|
Comp.TagDropdown.getSelected model.tagModel
|
|
|
|
n =
|
|
{ s
|
|
| abbrev = model.abbrev
|
|
, description = model.description
|
|
, enabled = model.enabled
|
|
, priority = Data.Priority.toName model.priority
|
|
, folder = model.folderId
|
|
, fileFilter = model.fileFilter
|
|
, language = model.language
|
|
, attachmentsOnly = model.attachmentsOnly
|
|
}
|
|
in
|
|
{ st | source = n, tags = TagList (List.length tags) tags }
|
|
|
|
|
|
type Msg
|
|
= SetAbbrev String
|
|
| SetSource SourceAndTags
|
|
| SetDescr String
|
|
| ToggleEnabled
|
|
| PrioDropdownMsg (Comp.FixedDropdown.Msg Priority)
|
|
| GetFolderResp (Result Http.Error FolderList)
|
|
| FolderDropdownMsg (Comp.Dropdown.Msg IdName)
|
|
| TagDropdownMsg Comp.TagDropdown.Msg
|
|
| SetFileFilter String
|
|
| LanguageMsg (Comp.Dropdown.Msg Language)
|
|
| ToggleAttachmentsOnly
|
|
|
|
|
|
|
|
--- Update
|
|
|
|
|
|
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
|
|
update flags msg model =
|
|
case msg of
|
|
SetSource t ->
|
|
let
|
|
stpost =
|
|
model.source
|
|
|
|
post =
|
|
stpost.source
|
|
|
|
np =
|
|
{ post
|
|
| id = t.source.id
|
|
, abbrev = t.source.abbrev
|
|
, description = t.source.description
|
|
, priority = t.source.priority
|
|
, enabled = t.source.enabled
|
|
, folder = t.source.folder
|
|
, fileFilter = t.source.fileFilter
|
|
, language = t.source.language
|
|
}
|
|
|
|
newModel =
|
|
{ model
|
|
| source = { stpost | source = np }
|
|
, abbrev = t.source.abbrev
|
|
, description = t.source.description
|
|
, priority =
|
|
Data.Priority.fromString t.source.priority
|
|
|> Maybe.withDefault Data.Priority.Low
|
|
, enabled = t.source.enabled
|
|
, folderId = t.source.folder
|
|
, fileFilter = t.source.fileFilter
|
|
, language = t.source.language
|
|
}
|
|
|
|
mkIdName id =
|
|
List.filterMap
|
|
(\f ->
|
|
if f.id == id then
|
|
Just (IdName id f.name)
|
|
|
|
else
|
|
Nothing
|
|
)
|
|
model.allFolders
|
|
|
|
sel =
|
|
case Maybe.map mkIdName t.source.folder of
|
|
Just idref ->
|
|
idref
|
|
|
|
Nothing ->
|
|
[]
|
|
|
|
langSel =
|
|
case Maybe.andThen Data.Language.fromString t.source.language of
|
|
Just lang ->
|
|
[ lang ]
|
|
|
|
Nothing ->
|
|
[]
|
|
|
|
tags =
|
|
Comp.TagDropdown.setSelected t.tags.items
|
|
in
|
|
Util.Update.andThen1
|
|
[ update flags (FolderDropdownMsg (Comp.Dropdown.SetSelection sel))
|
|
, update flags (TagDropdownMsg tags)
|
|
, update flags (LanguageMsg (Comp.Dropdown.SetSelection langSel))
|
|
]
|
|
newModel
|
|
|
|
ToggleEnabled ->
|
|
( { model | enabled = not model.enabled }, Cmd.none )
|
|
|
|
ToggleAttachmentsOnly ->
|
|
( { model | attachmentsOnly = not model.attachmentsOnly }, Cmd.none )
|
|
|
|
SetAbbrev n ->
|
|
( { model | abbrev = n }, Cmd.none )
|
|
|
|
SetDescr d ->
|
|
( { model | description = Util.Maybe.fromString d }
|
|
, Cmd.none
|
|
)
|
|
|
|
PrioDropdownMsg m ->
|
|
let
|
|
( m2, p2 ) =
|
|
Comp.FixedDropdown.update m model.priorityModel
|
|
in
|
|
( { model
|
|
| priorityModel = m2
|
|
, priority = Maybe.withDefault model.priority p2
|
|
}
|
|
, Cmd.none
|
|
)
|
|
|
|
GetFolderResp (Ok fs) ->
|
|
let
|
|
model_ =
|
|
{ model | allFolders = fs.items }
|
|
|
|
mkIdName fitem =
|
|
IdName fitem.id fitem.name
|
|
|
|
opts =
|
|
fs.items
|
|
|> List.map mkIdName
|
|
|> Comp.Dropdown.SetOptions
|
|
in
|
|
update flags (FolderDropdownMsg opts) model_
|
|
|
|
GetFolderResp (Err _) ->
|
|
( model, Cmd.none )
|
|
|
|
FolderDropdownMsg m ->
|
|
let
|
|
( m2, c2 ) =
|
|
Comp.Dropdown.update m model.folderModel
|
|
|
|
newModel =
|
|
{ model | folderModel = m2 }
|
|
|
|
idref =
|
|
Comp.Dropdown.getSelected m2 |> List.head
|
|
|
|
model_ =
|
|
if isDropdownChangeMsg m then
|
|
{ newModel | folderId = Maybe.map .id idref }
|
|
|
|
else
|
|
newModel
|
|
in
|
|
( model_, Cmd.map FolderDropdownMsg c2 )
|
|
|
|
TagDropdownMsg lm ->
|
|
let
|
|
( m2, c2 ) =
|
|
Comp.TagDropdown.update lm model.tagModel
|
|
|
|
newModel =
|
|
{ model | tagModel = m2 }
|
|
in
|
|
( newModel, Cmd.map TagDropdownMsg c2 )
|
|
|
|
SetFileFilter d ->
|
|
( { model | fileFilter = Util.Maybe.fromString d }
|
|
, Cmd.none
|
|
)
|
|
|
|
LanguageMsg lm ->
|
|
let
|
|
( dm, dc ) =
|
|
Comp.Dropdown.update lm model.languageModel
|
|
|
|
newModel =
|
|
{ model | languageModel = dm }
|
|
|
|
lang =
|
|
Comp.Dropdown.getSelected dm |> List.head
|
|
|
|
model_ =
|
|
if isDropdownChangeMsg lm then
|
|
{ newModel | language = Maybe.map Data.Language.toIso3 lang }
|
|
|
|
else
|
|
newModel
|
|
in
|
|
( model_
|
|
, Cmd.map LanguageMsg dc
|
|
)
|
|
|
|
|
|
|
|
--- View2
|
|
|
|
|
|
view2 : Flags -> Texts -> UiSettings -> Model -> Html Msg
|
|
view2 flags texts settings model =
|
|
let
|
|
folderCfg =
|
|
{ makeOption = mkFolderOption flags model.allFolders
|
|
, placeholder = texts.basics.selectPlaceholder
|
|
, labelColor = \_ -> \_ -> ""
|
|
, style = DS.mainStyle
|
|
}
|
|
|
|
languageCfg =
|
|
{ makeOption =
|
|
\a ->
|
|
{ text = texts.languageLabel a
|
|
, additional = ""
|
|
}
|
|
, placeholder = texts.basics.selectPlaceholder
|
|
, labelColor = \_ -> \_ -> ""
|
|
, style = DS.mainStyle
|
|
}
|
|
|
|
priorityCfg =
|
|
{ display = Data.Priority.toName
|
|
, icon = \_ -> Nothing
|
|
, style = DS.mainStyle
|
|
, selectPlaceholder = texts.basics.selectPlaceholder
|
|
}
|
|
in
|
|
div [ class "flex flex-col" ]
|
|
[ div [ class "mb-4" ]
|
|
[ label
|
|
[ for "source-abbrev"
|
|
, class S.inputLabel
|
|
]
|
|
[ text texts.basics.name
|
|
, B.inputRequired
|
|
]
|
|
, input
|
|
[ type_ "text"
|
|
, id "source-abbrev"
|
|
, onInput SetAbbrev
|
|
, placeholder texts.basics.name
|
|
, value model.abbrev
|
|
, class S.textInput
|
|
, classList [ ( S.inputErrorBorder, not (isValid model) ) ]
|
|
]
|
|
[]
|
|
]
|
|
, div [ class "mb-4" ]
|
|
[ label
|
|
[ for "source-descr"
|
|
, class S.inputLabel
|
|
]
|
|
[ text texts.description
|
|
]
|
|
, textarea
|
|
[ onInput SetDescr
|
|
, model.description |> Maybe.withDefault "" |> value
|
|
, rows 3
|
|
, class S.textAreaInput
|
|
, id "source-descr"
|
|
]
|
|
[]
|
|
]
|
|
, div [ class "mb-4" ]
|
|
[ label
|
|
[ class "inline-flex items-center"
|
|
, for "source-enabled"
|
|
]
|
|
[ input
|
|
[ type_ "checkbox"
|
|
, onCheck (\_ -> ToggleEnabled)
|
|
, checked model.enabled
|
|
, class S.checkboxInput
|
|
, id "source-enabled"
|
|
]
|
|
[]
|
|
, span [ class "ml-2" ]
|
|
[ text texts.enabled
|
|
]
|
|
]
|
|
]
|
|
, div [ class "mb-4" ]
|
|
[ label [ class S.inputLabel ]
|
|
[ text texts.priority
|
|
]
|
|
, Html.map PrioDropdownMsg
|
|
(Comp.FixedDropdown.viewStyled2
|
|
priorityCfg
|
|
False
|
|
(Just model.priority)
|
|
model.priorityModel
|
|
)
|
|
, div [ class "opacity-50 text-sm" ]
|
|
[ text texts.priorityInfo
|
|
]
|
|
]
|
|
, div
|
|
[ class S.header2
|
|
, class "mt-6"
|
|
]
|
|
[ text texts.metadata
|
|
]
|
|
, div
|
|
[ class S.message
|
|
, class "mb-4"
|
|
]
|
|
[ text texts.metadataInfoText
|
|
]
|
|
, div [ class "mb-4" ]
|
|
[ label [ class S.inputLabel ]
|
|
[ text texts.basics.folder
|
|
]
|
|
, Html.map FolderDropdownMsg
|
|
(Comp.Dropdown.view2
|
|
folderCfg
|
|
settings
|
|
model.folderModel
|
|
)
|
|
, div [ class "opacity-50 text-sm" ]
|
|
[ text texts.folderInfo
|
|
]
|
|
, div
|
|
[ classList
|
|
[ ( "hidden", isFolderMember2 model )
|
|
]
|
|
, class S.message
|
|
]
|
|
[ Markdown.toHtml [] texts.basics.folderNotOwnerWarning
|
|
]
|
|
]
|
|
, div [ class "mb-4" ]
|
|
[ label [ class S.inputLabel ]
|
|
[ text texts.basics.tags
|
|
]
|
|
, Html.map TagDropdownMsg
|
|
(Comp.TagDropdown.view
|
|
texts.tagDropdown
|
|
settings
|
|
DS.mainStyle
|
|
model.tagModel
|
|
)
|
|
, div [ class "opacity-50 text-sm" ]
|
|
[ text texts.tagsInfo
|
|
]
|
|
]
|
|
, div
|
|
[ class "mb-4"
|
|
]
|
|
[ label [ class S.inputLabel ]
|
|
[ text texts.fileFilter ]
|
|
, input
|
|
[ type_ "text"
|
|
, onInput SetFileFilter
|
|
, placeholder texts.fileFilter
|
|
, model.fileFilter
|
|
|> Maybe.withDefault ""
|
|
|> value
|
|
, class S.textInput
|
|
]
|
|
[]
|
|
, div [ class "opacity-50 text-sm" ]
|
|
[ Markdown.toHtml [] texts.fileFilterInfo
|
|
]
|
|
]
|
|
, div [ class "mb-4" ]
|
|
[ label
|
|
[ class "inline-flex items-center"
|
|
, for "attachments-only"
|
|
]
|
|
[ input
|
|
[ type_ "checkbox"
|
|
, onCheck (\_ -> ToggleAttachmentsOnly)
|
|
, checked model.attachmentsOnly
|
|
, class S.checkboxInput
|
|
, id "attachments-only"
|
|
]
|
|
[]
|
|
, span [ class "ml-2" ]
|
|
[ text texts.attachmentsOnly
|
|
]
|
|
]
|
|
]
|
|
, div [ class "mb-4" ]
|
|
[ label [ class S.inputLabel ]
|
|
[ text (texts.language ++ ":")
|
|
]
|
|
, Html.map LanguageMsg
|
|
(Comp.Dropdown.view2
|
|
languageCfg
|
|
settings
|
|
model.languageModel
|
|
)
|
|
, div [ class "opacity-50 text-sm" ]
|
|
[ text texts.languageInfo
|
|
]
|
|
]
|
|
]
|
|
|
|
|
|
isFolderMember2 : Model -> Bool
|
|
isFolderMember2 model =
|
|
let
|
|
selected =
|
|
Comp.Dropdown.getSelected model.folderModel
|
|
|> List.head
|
|
|> Maybe.map .id
|
|
in
|
|
Util.Folder.isFolderMember model.allFolders selected
|