docspell/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm

730 lines
23 KiB
Elm

module Comp.UiSettingsForm exposing
( Model
, Msg
, init
, update
, view2
)
import Api
import Api.Model.TagList exposing (TagList)
import Comp.BasicSizeField
import Comp.ColorTagger
import Comp.FieldListSelect
import Comp.FixedDropdown
import Comp.IntField
import Comp.MenuBar as MB
import Comp.Tabs
import Data.BasicSize exposing (BasicSize)
import Data.Color exposing (Color)
import Data.DropdownStyle as DS
import Data.Fields exposing (Field)
import Data.Flags exposing (Flags)
import Data.ItemTemplate as IT exposing (ItemTemplate)
import Data.UiSettings exposing (ItemPattern, Pos(..), UiSettings)
import Dict exposing (Dict)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick, onInput)
import Http
import Markdown
import Messages
import Messages.Comp.UiSettingsForm exposing (Texts)
import Messages.UiLanguage exposing (UiLanguage)
import Set exposing (Set)
import Styles as S
import Util.Maybe
import Util.Tag
type alias Model =
{ itemSearchPageSize : Maybe Int
, searchPageSizeModel : Comp.IntField.Model
, tagColors : Dict String Color
, tagColorModel : Comp.ColorTagger.Model
, nativePdfPreview : Bool
, itemSearchNoteLength : Maybe Int
, searchNoteLengthModel : Comp.IntField.Model
, searchMenuFolderCount : Maybe Int
, searchMenuFolderCountModel : Comp.IntField.Model
, searchMenuTagCount : Maybe Int
, searchMenuTagCountModel : Comp.IntField.Model
, searchMenuTagCatCount : Maybe Int
, searchMenuTagCatCountModel : Comp.IntField.Model
, formFields : List Field
, itemDetailShortcuts : Bool
, cardPreviewSize : BasicSize
, cardTitlePattern : PatternModel
, cardSubtitlePattern : PatternModel
, showPatternHelp : Bool
, searchStatsVisible : Bool
, sideMenuVisible : Bool
, powerSearchEnabled : Bool
, uiLangModel : Comp.FixedDropdown.Model UiLanguage
, uiLang : UiLanguage
, openTabs : Set String
}
type alias PatternModel =
{ pattern : Maybe String
, current : ItemTemplate
, result : Result String ItemTemplate
}
initPatternModel : ItemPattern -> PatternModel
initPatternModel ip =
{ pattern = Just ip.pattern
, current = ip.template
, result = Ok ip.template
}
updatePatternModel : PatternModel -> String -> PatternModel
updatePatternModel pm str =
let
result =
case IT.readTemplate str of
Just t ->
Ok t
Nothing ->
Err "Template invalid, check for unclosed variables."
p =
Util.Maybe.fromString str
in
{ pattern = p
, current = Result.withDefault pm.current result
, result = result
}
init : Flags -> UiSettings -> ( Model, Cmd Msg )
init flags settings =
( { itemSearchPageSize = Just settings.itemSearchPageSize
, searchPageSizeModel =
Comp.IntField.init
(Just 10)
(Just flags.config.maxPageSize)
False
, tagColors = settings.tagCategoryColors
, tagColorModel =
Comp.ColorTagger.init
[]
Data.Color.all
, nativePdfPreview = settings.nativePdfPreview
, itemSearchNoteLength = Just settings.itemSearchNoteLength
, searchNoteLengthModel =
Comp.IntField.init
(Just 0)
(Just flags.config.maxNoteLength)
False
, searchMenuFolderCount = Just settings.searchMenuFolderCount
, searchMenuFolderCountModel =
Comp.IntField.init
(Just 0)
(Just 2000)
False
, searchMenuTagCount = Just settings.searchMenuTagCount
, searchMenuTagCountModel =
Comp.IntField.init
(Just 0)
(Just 2000)
False
, searchMenuTagCatCount = Just settings.searchMenuTagCatCount
, searchMenuTagCatCountModel =
Comp.IntField.init
(Just 0)
(Just 2000)
False
, formFields = settings.formFields
, itemDetailShortcuts = settings.itemDetailShortcuts
, cardPreviewSize = settings.cardPreviewSize
, cardTitlePattern = initPatternModel settings.cardTitleTemplate
, cardSubtitlePattern = initPatternModel settings.cardSubtitleTemplate
, showPatternHelp = False
, searchStatsVisible = settings.searchStatsVisible
, sideMenuVisible = settings.sideMenuVisible
, powerSearchEnabled = settings.powerSearchEnabled
, uiLang = settings.uiLang
, uiLangModel =
Comp.FixedDropdown.init Messages.UiLanguage.all
, openTabs = Set.empty
}
, Api.getTags flags "" GetTagsResp
)
type Msg
= SearchPageSizeMsg Comp.IntField.Msg
| TagColorMsg Comp.ColorTagger.Msg
| GetTagsResp (Result Http.Error TagList)
| TogglePdfPreview
| NoteLengthMsg Comp.IntField.Msg
| SearchMenuFolderMsg Comp.IntField.Msg
| SearchMenuTagMsg Comp.IntField.Msg
| SearchMenuTagCatMsg Comp.IntField.Msg
| FieldListMsg Comp.FieldListSelect.Msg
| ToggleItemDetailShortcuts
| CardPreviewSizeMsg Comp.BasicSizeField.Msg
| SetCardTitlePattern String
| SetCardSubtitlePattern String
| TogglePatternHelpMsg
| ToggleSearchStatsVisible
| ToggleAkkordionTab String
| ToggleSideMenuVisible
| TogglePowerSearch
| UiLangMsg (Comp.FixedDropdown.Msg UiLanguage)
--- Update
update : UiSettings -> Msg -> Model -> ( Model, Maybe UiSettings )
update sett msg model =
case msg of
SearchPageSizeMsg lm ->
let
( m, n ) =
Comp.IntField.update lm model.searchPageSizeModel
nextSettings =
Maybe.map (\sz -> { sett | itemSearchPageSize = sz }) n
model_ =
{ model
| searchPageSizeModel = m
, itemSearchPageSize = n
}
in
( model_, nextSettings )
NoteLengthMsg lm ->
let
( m, n ) =
Comp.IntField.update lm model.searchNoteLengthModel
nextSettings =
Maybe.map (\len -> { sett | itemSearchNoteLength = len }) n
model_ =
{ model
| searchNoteLengthModel = m
, itemSearchNoteLength = n
}
in
( model_, nextSettings )
SearchMenuFolderMsg lm ->
let
( m, n ) =
Comp.IntField.update lm model.searchMenuFolderCountModel
nextSettings =
Maybe.map (\len -> { sett | searchMenuFolderCount = len }) n
model_ =
{ model
| searchMenuFolderCountModel = m
, searchMenuFolderCount = n
}
in
( model_, nextSettings )
SearchMenuTagMsg lm ->
let
( m, n ) =
Comp.IntField.update lm model.searchMenuTagCountModel
nextSettings =
Maybe.map (\len -> { sett | searchMenuTagCount = len }) n
model_ =
{ model
| searchMenuTagCountModel = m
, searchMenuTagCount = n
}
in
( model_, nextSettings )
SearchMenuTagCatMsg lm ->
let
( m, n ) =
Comp.IntField.update lm model.searchMenuTagCatCountModel
nextSettings =
Maybe.map (\len -> { sett | searchMenuTagCatCount = len }) n
model_ =
{ model
| searchMenuTagCatCountModel = m
, searchMenuTagCatCount = n
}
in
( model_, nextSettings )
TagColorMsg lm ->
let
( m_, d_ ) =
Comp.ColorTagger.update lm model.tagColorModel
nextSettings =
Maybe.map (\tc -> { sett | tagCategoryColors = tc }) d_
model_ =
{ model
| tagColorModel = m_
, tagColors = Maybe.withDefault model.tagColors d_
}
in
( model_, nextSettings )
TogglePdfPreview ->
let
flag =
not model.nativePdfPreview
in
( { model | nativePdfPreview = flag }
, Just { sett | nativePdfPreview = flag }
)
GetTagsResp (Ok tl) ->
let
categories =
Util.Tag.getCategories tl.items
in
( { model
| tagColorModel =
Comp.ColorTagger.init
categories
Data.Color.all
}
, Nothing
)
GetTagsResp (Err _) ->
( model, Nothing )
FieldListMsg lm ->
let
selected =
Comp.FieldListSelect.update lm model.formFields
newSettings =
{ sett | formFields = selected }
in
( { model | formFields = selected }
, if selected /= model.formFields then
Just newSettings
else
Nothing
)
ToggleItemDetailShortcuts ->
let
flag =
not model.itemDetailShortcuts
in
( { model | itemDetailShortcuts = flag }
, Just { sett | itemDetailShortcuts = flag }
)
CardPreviewSizeMsg lm ->
let
next =
Comp.BasicSizeField.update lm
|> Maybe.withDefault model.cardPreviewSize
newSettings =
if next /= model.cardPreviewSize then
Just { sett | cardPreviewSize = next }
else
Nothing
in
( { model | cardPreviewSize = next }
, newSettings
)
SetCardTitlePattern str ->
let
pm =
model.cardTitlePattern
pm_ =
updatePatternModel pm str
newSettings =
if pm_.pattern /= Just sett.cardTitleTemplate.pattern then
Just
{ sett
| cardTitleTemplate =
ItemPattern
(Maybe.withDefault "" pm_.pattern)
pm_.current
}
else
Nothing
in
( { model | cardTitlePattern = pm_ }, newSettings )
SetCardSubtitlePattern str ->
let
pm =
model.cardSubtitlePattern
pm_ =
updatePatternModel pm str
newSettings =
if pm_.pattern /= Just sett.cardSubtitleTemplate.pattern then
Just
{ sett
| cardSubtitleTemplate =
ItemPattern
(Maybe.withDefault "" pm_.pattern)
pm_.current
}
else
Nothing
in
( { model | cardSubtitlePattern = pm_ }, newSettings )
TogglePatternHelpMsg ->
( { model | showPatternHelp = not model.showPatternHelp }, Nothing )
ToggleSearchStatsVisible ->
let
flag =
not model.searchStatsVisible
in
( { model | searchStatsVisible = flag }
, Just { sett | searchStatsVisible = flag }
)
ToggleAkkordionTab name ->
let
tabs =
if Set.member name model.openTabs then
Set.remove name model.openTabs
else
Set.insert name model.openTabs
in
( { model | openTabs = tabs }
, Nothing
)
ToggleSideMenuVisible ->
let
next =
not model.sideMenuVisible
in
( { model | sideMenuVisible = next }
, Just { sett | sideMenuVisible = next }
)
TogglePowerSearch ->
let
next =
not model.powerSearchEnabled
in
( { model | powerSearchEnabled = next }
, Just { sett | powerSearchEnabled = next }
)
UiLangMsg lm ->
let
( m, sel ) =
Comp.FixedDropdown.update lm model.uiLangModel
newLang =
Maybe.withDefault model.uiLang sel
in
( { model | uiLangModel = m, uiLang = newLang }
, if newLang == model.uiLang then
Nothing
else
Just { sett | uiLang = newLang }
)
--- View2
tagColorViewOpts2 : Texts -> Comp.ColorTagger.ViewOpts
tagColorViewOpts2 texts =
{ renderItem =
\( name, v ) ->
span [ class "flex inline-flex items-center" ]
[ span [ class "mr-2" ] [ text name ]
, span [ class (" label " ++ Data.Color.toString2 v) ]
[ text (texts.colorLabel v)
]
]
, colorLabel = texts.colorLabel
, label = texts.chooseTagColorLabel
, description = Just texts.tagColorDescription
, selectPlaceholder = texts.basics.selectPlaceholder
}
view2 : Texts -> Flags -> UiSettings -> Model -> Html Msg
view2 texts flags settings model =
let
state tab =
if Set.member tab.name model.openTabs then
Comp.Tabs.Open
else
Comp.Tabs.Closed
in
div [ class "flex flex-col" ]
[ Comp.Tabs.akkordion
Comp.Tabs.defaultStyle
(\t -> ( state t, ToggleAkkordionTab t.name ))
(settingFormTabs texts flags settings model)
]
settingFormTabs : Texts -> Flags -> UiSettings -> Model -> List (Comp.Tabs.Tab Msg)
settingFormTabs texts flags _ model =
let
langCfg =
{ display = \lang -> Messages.get lang |> .label
, icon = \lang -> Just (Messages.get lang |> .flagIcon)
, style = DS.mainStyle
, selectPlaceholder = texts.basics.selectPlaceholder
}
in
[ { name = "general"
, title = texts.general
, titleRight = []
, info = Nothing
, body =
[ div [ class "mb-4 " ]
[ MB.viewItem <|
MB.Checkbox
{ id = "uisetting-sidemenu-visible"
, label = texts.showSideMenuByDefault
, tagger = \_ -> ToggleSideMenuVisible
, value = model.sideMenuVisible
}
]
, div [ class "mb-4" ]
[ label [ class S.inputLabel ] [ text texts.uiLanguage ]
, Html.map UiLangMsg
(Comp.FixedDropdown.viewStyled2
langCfg
False
(Just model.uiLang)
model.uiLangModel
)
]
]
}
, { name = "item-search"
, title = texts.itemSearch
, titleRight = []
, info = Nothing
, body =
[ Html.map SearchPageSizeMsg
(Comp.IntField.view
{ label = texts.maxResultsPerPage
, info = texts.maxResultsPerPageInfo flags.config.maxPageSize
, number = model.itemSearchPageSize
, classes = "mb-4"
}
model.searchPageSizeModel
)
, div [ class "mb-4" ]
[ MB.viewItem <|
MB.Checkbox
{ id = "uisetting-searchstats-visible"
, value = model.searchStatsVisible
, tagger = \_ -> ToggleSearchStatsVisible
, label = texts.showBasicSearchStatsByDefault
}
]
, div [ class "mb-4" ]
[ MB.viewItem <|
MB.Checkbox
{ id = "uisetting-powersearch-enabled"
, value = model.powerSearchEnabled
, tagger = \_ -> TogglePowerSearch
, label = texts.enablePowerSearch
}
]
]
}
, { name = "item-cards"
, title = texts.itemCards
, titleRight = []
, info = Nothing
, body =
[ Html.map NoteLengthMsg
(Comp.IntField.view
{ label = texts.maxNoteSize
, info = texts.maxNoteSizeInfo flags.config.maxNoteLength
, number = model.itemSearchNoteLength
, classes = "mb-4"
}
model.searchNoteLengthModel
)
, Html.map CardPreviewSizeMsg
(Comp.BasicSizeField.view2
"mb-4"
texts.sizeOfItemPreview
model.cardPreviewSize
)
, div [ class "mb-4" ]
[ label [ class S.inputLabel ]
[ text texts.cardTitlePattern
, a
[ class "float-right"
, class S.link
, title texts.togglePatternHelpText
, href "#"
, onClick TogglePatternHelpMsg
]
[ i [ class "fa fa-question" ] []
]
]
, input
[ type_ "text"
, Maybe.withDefault "" model.cardTitlePattern.pattern |> value
, onInput SetCardTitlePattern
, class S.textInput
]
[]
]
, div [ class "mb-4" ]
[ label [ class S.inputLabel ]
[ text texts.cardSubtitlePattern
, a
[ class "float-right"
, class S.link
, title texts.togglePatternHelpText
, href "#"
, onClick TogglePatternHelpMsg
]
[ i [ class "fa fa-question" ] []
]
]
, input
[ type_ "text"
, Maybe.withDefault "" model.cardSubtitlePattern.pattern |> value
, onInput SetCardSubtitlePattern
, class S.textInput
]
[]
]
, Markdown.toHtml
[ classList
[ ( "hidden", not model.showPatternHelp )
]
, class S.message
, class "markdown-preview"
]
texts.templateHelpMessage
]
}
, { name = "search-menu"
, title = texts.searchMenu
, titleRight = []
, info = Nothing
, body =
[ Html.map SearchMenuTagMsg
(Comp.IntField.view
{ label = texts.searchMenuTagCount
, info = texts.searchMenuTagCountInfo
, number = model.searchMenuTagCount
, classes = "mb-4"
}
model.searchMenuTagCountModel
)
, Html.map SearchMenuTagCatMsg
(Comp.IntField.view
{ label = texts.searchMenuCatCount
, info = texts.searchMenuCatCountInfo
, number = model.searchMenuTagCatCount
, classes = "mb-4"
}
model.searchMenuTagCatCountModel
)
, Html.map SearchMenuFolderMsg
(Comp.IntField.view
{ label = texts.searchMenuFolderCount
, info = texts.searchMenuFolderCountInfo
, number = model.searchMenuFolderCount
, classes = "mb-4"
}
model.searchMenuFolderCountModel
)
]
}
, { name = "item-detail"
, title = texts.itemDetail
, titleRight = []
, info = Nothing
, body =
[ div [ class "mb-4" ]
[ MB.viewItem <|
MB.Checkbox
{ tagger = \_ -> TogglePdfPreview
, label = texts.browserNativePdfView
, value = model.nativePdfPreview
, id = "uisetting-pdfpreview-toggle"
}
]
, div [ class "mb-4" ]
[ MB.viewItem <|
MB.Checkbox
{ tagger = \_ -> ToggleItemDetailShortcuts
, label = texts.keyboardShortcutLabel
, value = model.itemDetailShortcuts
, id = "uisetting-itemdetailshortcuts-toggle"
}
]
]
}
, { name = "tag-category-colors"
, title = texts.tagCategoryColors
, titleRight = []
, info = Nothing
, body =
[ Html.map TagColorMsg
(Comp.ColorTagger.view2
model.tagColors
(tagColorViewOpts2 texts)
model.tagColorModel
)
]
}
, { name = "fields"
, title = texts.fields
, titleRight = []
, info = Nothing
, body =
[ span [ class "opacity-50 text-sm" ]
[ text texts.fieldsInfo
]
, Html.map FieldListMsg
(Comp.FieldListSelect.view2
{ classes = "px-2"
, fieldLabel = texts.fieldLabel
}
model.formFields
)
]
}
]