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 ) ] } ]