mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-04-15 07:09:33 +00:00
Client settings per collective and user
Client settings can be stored at the user and and the collective. The settings used in the application are merged from these two settings, where any user setting takes precedence. The form can now manage both variants. Refs: #838
This commit is contained in:
parent
ee927096a4
commit
c29ce73dd0
@ -64,6 +64,7 @@ module Api exposing
|
||||
, getChannels
|
||||
, getChannelsIgnoreError
|
||||
, getClientSettings
|
||||
, getClientSettingsRaw
|
||||
, getCollective
|
||||
, getCollectiveSettings
|
||||
, getContacts
|
||||
@ -136,6 +137,7 @@ module Api exposing
|
||||
, restoreItem
|
||||
, sampleEvent
|
||||
, saveClientSettings
|
||||
, saveUserClientSettingsBy
|
||||
, searchShare
|
||||
, searchShareStats
|
||||
, sendMail
|
||||
@ -296,7 +298,7 @@ import Data.OrganizationOrder exposing (OrganizationOrder)
|
||||
import Data.PersonOrder exposing (PersonOrder)
|
||||
import Data.Priority exposing (Priority)
|
||||
import Data.TagOrder exposing (TagOrder)
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import Data.UiSettings exposing (StoredUiSettings, UiSettings)
|
||||
import File exposing (File)
|
||||
import Http
|
||||
import Json.Decode as JsonDecode
|
||||
@ -2335,6 +2337,13 @@ getItemProposals flags item receive =
|
||||
--- Client Settings
|
||||
|
||||
|
||||
uiSettingsPath : AccountScope -> String
|
||||
uiSettingsPath scope =
|
||||
Data.AccountScope.fold "/api/v1/sec/clientSettings/user/webClient"
|
||||
"/api/v1/sec/clientSettings/collective/webClient"
|
||||
scope
|
||||
|
||||
|
||||
getClientSettings : Flags -> (Result Http.Error UiSettings -> msg) -> Cmd msg
|
||||
getClientSettings flags receive =
|
||||
let
|
||||
@ -2346,29 +2355,91 @@ getClientSettings flags receive =
|
||||
Data.UiSettings.storedUiSettingsDecoder
|
||||
in
|
||||
Http2.authGet
|
||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/clientSettings/user/webClient"
|
||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/clientSettings/webClient"
|
||||
, account = getAccount flags
|
||||
, expect = Http.expectJson receive decoder
|
||||
}
|
||||
|
||||
|
||||
saveClientSettings : Flags -> UiSettings -> (Result Http.Error BasicResult -> msg) -> Cmd msg
|
||||
saveClientSettings flags settings receive =
|
||||
getClientSettingsTaskFor : Flags -> AccountScope -> Task.Task Http.Error StoredUiSettings
|
||||
getClientSettingsTaskFor flags scope =
|
||||
let
|
||||
storedSettings =
|
||||
Data.UiSettings.toStoredUiSettings settings
|
||||
|
||||
encode =
|
||||
Data.UiSettings.storedUiSettingsEncode storedSettings
|
||||
path =
|
||||
uiSettingsPath scope
|
||||
in
|
||||
Http2.authPut
|
||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/clientSettings/user/webClient"
|
||||
Http2.authTask
|
||||
{ method = "GET"
|
||||
, url = flags.config.baseUrl ++ path
|
||||
, account = getAccount flags
|
||||
, body = Http.jsonBody encode
|
||||
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
||||
, body = Http.emptyBody
|
||||
, resolver = Http2.jsonResolver Data.UiSettings.storedUiSettingsDecoder
|
||||
, headers = []
|
||||
, timeout = Nothing
|
||||
}
|
||||
|
||||
|
||||
getClientSettingsRaw : Flags -> (Result Http.Error ( StoredUiSettings, StoredUiSettings ) -> msg) -> Cmd msg
|
||||
getClientSettingsRaw flags receive =
|
||||
let
|
||||
coll =
|
||||
getClientSettingsTaskFor flags Data.AccountScope.Collective
|
||||
|
||||
user =
|
||||
getClientSettingsTaskFor flags Data.AccountScope.User
|
||||
in
|
||||
Task.map2 Tuple.pair coll user |> Task.attempt receive
|
||||
|
||||
|
||||
saveClientSettingsTask :
|
||||
Flags
|
||||
-> StoredUiSettings
|
||||
-> AccountScope
|
||||
-> Task.Task Http.Error BasicResult
|
||||
saveClientSettingsTask flags settings scope =
|
||||
let
|
||||
encoded =
|
||||
Data.UiSettings.storedUiSettingsEncode settings
|
||||
|
||||
path =
|
||||
uiSettingsPath scope
|
||||
in
|
||||
Http2.authTask
|
||||
{ method = "PUT"
|
||||
, url = flags.config.baseUrl ++ path
|
||||
, account = getAccount flags
|
||||
, body = Http.jsonBody encoded
|
||||
, resolver = Http2.jsonResolver Api.Model.BasicResult.decoder
|
||||
, headers = []
|
||||
, timeout = Nothing
|
||||
}
|
||||
|
||||
|
||||
saveClientSettings :
|
||||
Flags
|
||||
-> StoredUiSettings
|
||||
-> AccountScope
|
||||
-> (Result Http.Error BasicResult -> msg)
|
||||
-> Cmd msg
|
||||
saveClientSettings flags settings scope receive =
|
||||
saveClientSettingsTask flags settings scope |> Task.attempt receive
|
||||
|
||||
|
||||
saveUserClientSettingsBy :
|
||||
Flags
|
||||
-> (StoredUiSettings -> StoredUiSettings)
|
||||
-> (Result Http.Error BasicResult -> msg)
|
||||
-> Cmd msg
|
||||
saveUserClientSettingsBy flags modify receive =
|
||||
let
|
||||
readTask =
|
||||
getClientSettingsTaskFor flags Data.AccountScope.User
|
||||
|
||||
save s =
|
||||
saveClientSettingsTask flags s Data.AccountScope.User
|
||||
in
|
||||
Task.andThen (modify >> save) readTask |> Task.attempt receive
|
||||
|
||||
|
||||
|
||||
--- Dashboards
|
||||
|
||||
|
@ -204,8 +204,7 @@ type Msg
|
||||
| ToggleDarkMode
|
||||
| ToggleLangMenu
|
||||
| SetLanguage UiLanguage
|
||||
| ClientSettingsSaveResp UiSettings (Result Http.Error BasicResult)
|
||||
| ReceiveBrowserSettings StoredUiSettings
|
||||
| ClientSettingsSaveResp (Result Http.Error BasicResult)
|
||||
| ReceiveWsMessage (Result String ServerEvent)
|
||||
| ToggleShowNewItemsArrived
|
||||
|
||||
|
@ -14,6 +14,7 @@ import Api
|
||||
import App.Data exposing (..)
|
||||
import Browser exposing (UrlRequest(..))
|
||||
import Browser.Navigation as Nav
|
||||
import Data.AppEvent exposing (AppEvent(..))
|
||||
import Data.Flags
|
||||
import Data.ServerEvent exposing (ServerEvent(..))
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
@ -81,15 +82,15 @@ updateWithSub msg model =
|
||||
next =
|
||||
Data.UiTheme.cycle settings.uiTheme
|
||||
|
||||
newSettings =
|
||||
{ settings | uiTheme = next }
|
||||
newSettings s =
|
||||
{ s | uiTheme = Just (Data.UiTheme.toString next) }
|
||||
in
|
||||
-- when authenticated, store it in settings only
|
||||
-- once new settings are successfully saved (the
|
||||
-- response is arrived), the ui is updated. so it
|
||||
-- is also updated on page refresh
|
||||
( { model | userMenuOpen = False }
|
||||
, Api.saveClientSettings model.flags newSettings (ClientSettingsSaveResp newSettings)
|
||||
, Api.saveUserClientSettingsBy model.flags newSettings ClientSettingsSaveResp
|
||||
, Sub.none
|
||||
)
|
||||
|
||||
@ -104,14 +105,14 @@ updateWithSub msg model =
|
||||
, Sub.none
|
||||
)
|
||||
|
||||
ClientSettingsSaveResp settings (Ok res) ->
|
||||
ClientSettingsSaveResp (Ok res) ->
|
||||
if res.success then
|
||||
applyClientSettings texts model settings
|
||||
( model, Api.getClientSettings model.flags GetUiSettings, Sub.none )
|
||||
|
||||
else
|
||||
( model, Cmd.none, Sub.none )
|
||||
|
||||
ClientSettingsSaveResp _ (Err _) ->
|
||||
ClientSettingsSaveResp (Err _) ->
|
||||
( model, Cmd.none, Sub.none )
|
||||
|
||||
ToggleLangMenu ->
|
||||
@ -307,13 +308,6 @@ updateWithSub msg model =
|
||||
GetUiSettings (Err _) ->
|
||||
( model, Cmd.none, Sub.none )
|
||||
|
||||
ReceiveBrowserSettings sett ->
|
||||
let
|
||||
lm =
|
||||
Page.UserSettings.Data.ReceiveBrowserSettings sett
|
||||
in
|
||||
updateUserSettings texts lm model
|
||||
|
||||
ReceiveWsMessage data ->
|
||||
case data of
|
||||
Ok (JobDone task) ->
|
||||
@ -342,7 +336,7 @@ updateWithSub msg model =
|
||||
Ok (JobsWaiting n) ->
|
||||
( { model | jobsWaiting = max 0 n }, Cmd.none, Sub.none )
|
||||
|
||||
Err err ->
|
||||
Err _ ->
|
||||
( model, Cmd.none, Sub.none )
|
||||
|
||||
ToggleShowNewItemsArrived ->
|
||||
@ -368,7 +362,6 @@ applyClientSettings texts model settings =
|
||||
, Sub.none
|
||||
)
|
||||
, updateDashboard texts Page.Dashboard.Data.reloadUiSettings
|
||||
, updateUserSettings texts Page.UserSettings.Data.UpdateSettings
|
||||
, updateSearch texts Page.Search.Data.UiSettingsUpdated
|
||||
, updateItemDetail texts Page.ItemDetail.Data.UiSettingsUpdated
|
||||
]
|
||||
@ -524,22 +517,21 @@ updateUserSettings texts lmsg model =
|
||||
model_ =
|
||||
{ model | userSettingsModel = result.model }
|
||||
|
||||
( lm2, lc2, s2 ) =
|
||||
case result.newSettings of
|
||||
Just sett ->
|
||||
applyClientSettings texts model_ sett
|
||||
lc =
|
||||
case result.appEvent of
|
||||
AppReloadUiSettings ->
|
||||
Api.getClientSettings model.flags GetUiSettings
|
||||
|
||||
Nothing ->
|
||||
( model_, Cmd.none, Sub.none )
|
||||
AppNothing ->
|
||||
Cmd.none
|
||||
in
|
||||
( lm2
|
||||
( model_
|
||||
, Cmd.batch
|
||||
[ Cmd.map UserSettingsMsg result.cmd
|
||||
, lc2
|
||||
, lc
|
||||
]
|
||||
, Sub.batch
|
||||
[ Sub.map UserSettingsMsg result.sub
|
||||
, s2
|
||||
]
|
||||
)
|
||||
|
||||
@ -595,22 +587,21 @@ updateSearch texts lmsg model =
|
||||
model_ =
|
||||
{ model | searchModel = result.model }
|
||||
|
||||
( lm, lc, ls ) =
|
||||
case result.newSettings of
|
||||
Just sett ->
|
||||
applyClientSettings texts model_ sett
|
||||
lc =
|
||||
case result.appEvent of
|
||||
AppReloadUiSettings ->
|
||||
Api.getClientSettings model.flags GetUiSettings
|
||||
|
||||
Nothing ->
|
||||
( model_, Cmd.none, Sub.none )
|
||||
AppNothing ->
|
||||
Cmd.none
|
||||
in
|
||||
( lm
|
||||
( model_
|
||||
, Cmd.batch
|
||||
[ Cmd.map SearchMsg result.cmd
|
||||
, lc
|
||||
]
|
||||
, Sub.batch
|
||||
[ Sub.map SearchMsg result.sub
|
||||
, ls
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -26,6 +26,7 @@ import Styles as S
|
||||
type Item msg
|
||||
= TextInput (TextInputData msg)
|
||||
| Checkbox (CheckboxData msg)
|
||||
| RadioButton (CheckboxData msg)
|
||||
| PrimaryButton (ButtonData msg)
|
||||
| SecondaryButton (ButtonData msg)
|
||||
| DeleteButton (ButtonData msg)
|
||||
@ -119,7 +120,7 @@ view1 classes mb =
|
||||
(List.map viewItem mb.start)
|
||||
|
||||
right =
|
||||
div [ class "flex-grow flex-row flex justify-end space-x-2 w-full" ]
|
||||
div [ class "flex-grow flex-row items-center flex justify-end space-x-2 w-full" ]
|
||||
(List.map viewItem mb.end)
|
||||
in
|
||||
div
|
||||
@ -139,7 +140,10 @@ viewItem item =
|
||||
makeInput model
|
||||
|
||||
Checkbox model ->
|
||||
makeCheckbox model
|
||||
makeCheckbox False model
|
||||
|
||||
RadioButton model ->
|
||||
makeCheckbox True model
|
||||
|
||||
PrimaryButton model ->
|
||||
makeButton [ ( S.primaryButton, True ) ] model
|
||||
@ -306,8 +310,8 @@ makeButton btnType model =
|
||||
(icon ++ label)
|
||||
|
||||
|
||||
makeCheckbox : CheckboxData msg -> Html msg
|
||||
makeCheckbox model =
|
||||
makeCheckbox : Bool -> CheckboxData msg -> Html msg
|
||||
makeCheckbox radio model =
|
||||
let
|
||||
withId list =
|
||||
if model.id == "" then
|
||||
@ -315,6 +319,13 @@ makeCheckbox model =
|
||||
|
||||
else
|
||||
id model.id :: list
|
||||
|
||||
fold rd ck =
|
||||
if radio then
|
||||
rd
|
||||
|
||||
else
|
||||
ck
|
||||
in
|
||||
div [ class "" ]
|
||||
[ label
|
||||
@ -323,10 +334,10 @@ makeCheckbox model =
|
||||
]
|
||||
[ input
|
||||
(withId
|
||||
[ type_ "checkbox"
|
||||
[ type_ (fold "radio" "checkbox")
|
||||
, onCheck model.tagger
|
||||
, checked model.value
|
||||
, class S.checkboxInput
|
||||
, class (fold S.radioInput S.checkboxInput)
|
||||
]
|
||||
)
|
||||
[]
|
||||
|
@ -9,6 +9,7 @@ module Comp.UiSettingsForm exposing
|
||||
( Model
|
||||
, Msg
|
||||
, init
|
||||
, toggleAllTabs
|
||||
, update
|
||||
, view2
|
||||
)
|
||||
@ -30,7 +31,7 @@ import Data.Flags exposing (Flags)
|
||||
import Data.ItemTemplate as IT exposing (ItemTemplate)
|
||||
import Data.Pdf exposing (PdfMode)
|
||||
import Data.TagOrder
|
||||
import Data.UiSettings exposing (ItemPattern, Pos(..), UiSettings)
|
||||
import Data.UiSettings exposing (ItemPattern, Pos(..), StoredUiSettings, UiSettings)
|
||||
import Dict exposing (Dict)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
@ -73,6 +74,7 @@ type alias Model =
|
||||
, uiLangModel : Comp.FixedDropdown.Model UiLanguage
|
||||
, uiLang : UiLanguage
|
||||
, openTabs : Set String
|
||||
, defaults : UiSettings
|
||||
}
|
||||
|
||||
|
||||
@ -111,61 +113,69 @@ updatePatternModel pm str =
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
, pdfMode = settings.pdfMode
|
||||
, pdfModeModel = Comp.FixedDropdown.init Data.Pdf.allModes
|
||||
, 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 "" Data.TagOrder.NameAsc GetTagsResp
|
||||
)
|
||||
initModel : Flags -> StoredUiSettings -> UiSettings -> Model
|
||||
initModel flags storedSettings defaults =
|
||||
let
|
||||
settings =
|
||||
Data.UiSettings.merge storedSettings defaults
|
||||
in
|
||||
{ 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
|
||||
, pdfMode = settings.pdfMode
|
||||
, pdfModeModel = Comp.FixedDropdown.init Data.Pdf.allModes
|
||||
, 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
|
||||
, defaults = defaults
|
||||
}
|
||||
|
||||
|
||||
init : Flags -> StoredUiSettings -> UiSettings -> ( Model, Cmd Msg )
|
||||
init flags storedSettings defaults =
|
||||
( initModel flags storedSettings defaults, Api.getTags flags "" Data.TagOrder.NameAsc GetTagsResp )
|
||||
|
||||
|
||||
type Msg
|
||||
@ -188,14 +198,61 @@ type Msg
|
||||
| TogglePowerSearch
|
||||
| UiLangMsg (Comp.FixedDropdown.Msg UiLanguage)
|
||||
| PdfModeMsg (Comp.FixedDropdown.Msg PdfMode)
|
||||
| ToggleAllTabs
|
||||
| ResetTab AkkordionTab
|
||||
|
||||
|
||||
toggleAllTabs : Msg
|
||||
toggleAllTabs =
|
||||
ToggleAllTabs
|
||||
|
||||
|
||||
type AkkordionTab
|
||||
= GeneralTab
|
||||
| SearchTab
|
||||
| CardsTab
|
||||
| SearchMenuTab
|
||||
| DetailTab
|
||||
| TagsTab
|
||||
| FieldsTab
|
||||
|
||||
|
||||
allTabs : List AkkordionTab
|
||||
allTabs =
|
||||
[ GeneralTab, SearchTab, CardsTab, SearchMenuTab, DetailTab, TagsTab, FieldsTab ]
|
||||
|
||||
|
||||
akkordionTabName : AkkordionTab -> String
|
||||
akkordionTabName tab =
|
||||
case tab of
|
||||
GeneralTab ->
|
||||
"general"
|
||||
|
||||
SearchTab ->
|
||||
"search"
|
||||
|
||||
CardsTab ->
|
||||
"item-cards"
|
||||
|
||||
SearchMenuTab ->
|
||||
"search-menu"
|
||||
|
||||
DetailTab ->
|
||||
"item-detail"
|
||||
|
||||
TagsTab ->
|
||||
"tags"
|
||||
|
||||
FieldsTab ->
|
||||
"fields"
|
||||
|
||||
|
||||
|
||||
--- Update
|
||||
|
||||
|
||||
update : UiSettings -> Msg -> Model -> ( Model, Maybe UiSettings )
|
||||
update sett msg model =
|
||||
update : Flags -> StoredUiSettings -> Msg -> Model -> ( Model, Maybe StoredUiSettings )
|
||||
update flags sett msg model =
|
||||
case msg of
|
||||
SearchPageSizeMsg lm ->
|
||||
let
|
||||
@ -203,7 +260,7 @@ update sett msg model =
|
||||
Comp.IntField.update lm model.searchPageSizeModel
|
||||
|
||||
nextSettings =
|
||||
Maybe.map (\sz -> { sett | itemSearchPageSize = sz }) n
|
||||
Maybe.map (\sz -> { sett | itemSearchPageSize = Just sz }) n
|
||||
|
||||
model_ =
|
||||
{ model
|
||||
@ -219,7 +276,7 @@ update sett msg model =
|
||||
Comp.IntField.update lm model.searchNoteLengthModel
|
||||
|
||||
nextSettings =
|
||||
Maybe.map (\len -> { sett | itemSearchNoteLength = len }) n
|
||||
Maybe.map (\len -> { sett | itemSearchNoteLength = Just len }) n
|
||||
|
||||
model_ =
|
||||
{ model
|
||||
@ -235,7 +292,7 @@ update sett msg model =
|
||||
Comp.IntField.update lm model.searchMenuFolderCountModel
|
||||
|
||||
nextSettings =
|
||||
Maybe.map (\len -> { sett | searchMenuFolderCount = len }) n
|
||||
Maybe.map (\len -> { sett | searchMenuFolderCount = Just len }) n
|
||||
|
||||
model_ =
|
||||
{ model
|
||||
@ -251,7 +308,7 @@ update sett msg model =
|
||||
Comp.IntField.update lm model.searchMenuTagCountModel
|
||||
|
||||
nextSettings =
|
||||
Maybe.map (\len -> { sett | searchMenuTagCount = len }) n
|
||||
Maybe.map (\len -> { sett | searchMenuTagCount = Just len }) n
|
||||
|
||||
model_ =
|
||||
{ model
|
||||
@ -267,7 +324,7 @@ update sett msg model =
|
||||
Comp.IntField.update lm model.searchMenuTagCatCountModel
|
||||
|
||||
nextSettings =
|
||||
Maybe.map (\len -> { sett | searchMenuTagCatCount = len }) n
|
||||
Maybe.map (\len -> { sett | searchMenuTagCatCount = Just len }) n
|
||||
|
||||
model_ =
|
||||
{ model
|
||||
@ -282,8 +339,13 @@ update sett msg model =
|
||||
( m_, d_ ) =
|
||||
Comp.ColorTagger.update lm model.tagColorModel
|
||||
|
||||
colors dict =
|
||||
Dict.map (\_ -> Data.Color.toString) dict
|
||||
|> Dict.toList
|
||||
|> Just
|
||||
|
||||
nextSettings =
|
||||
Maybe.map (\tc -> { sett | tagCategoryColors = tc }) d_
|
||||
Maybe.map (\tc -> { sett | tagCategoryColors = colors tc }) d_
|
||||
|
||||
model_ =
|
||||
{ model
|
||||
@ -316,7 +378,11 @@ update sett msg model =
|
||||
Comp.FieldListSelect.update lm model.formFields
|
||||
|
||||
newSettings =
|
||||
{ sett | formFields = selected }
|
||||
{ sett
|
||||
| formFields =
|
||||
List.map Data.Fields.toString selected
|
||||
|> Just
|
||||
}
|
||||
in
|
||||
( { model | formFields = selected }
|
||||
, if selected /= model.formFields then
|
||||
@ -332,7 +398,7 @@ update sett msg model =
|
||||
not model.itemDetailShortcuts
|
||||
in
|
||||
( { model | itemDetailShortcuts = flag }
|
||||
, Just { sett | itemDetailShortcuts = flag }
|
||||
, Just { sett | itemDetailShortcuts = Just flag }
|
||||
)
|
||||
|
||||
CardPreviewSizeMsg lm ->
|
||||
@ -343,7 +409,13 @@ update sett msg model =
|
||||
|
||||
newSettings =
|
||||
if next /= model.cardPreviewSize then
|
||||
Just { sett | cardPreviewSize = next }
|
||||
Just
|
||||
{ sett
|
||||
| cardPreviewSize =
|
||||
next
|
||||
|> Data.BasicSize.asString
|
||||
|> Just
|
||||
}
|
||||
|
||||
else
|
||||
Nothing
|
||||
@ -361,14 +433,8 @@ update sett msg model =
|
||||
updatePatternModel pm str
|
||||
|
||||
newSettings =
|
||||
if pm_.pattern /= Just sett.cardTitleTemplate.pattern then
|
||||
Just
|
||||
{ sett
|
||||
| cardTitleTemplate =
|
||||
ItemPattern
|
||||
(Maybe.withDefault "" pm_.pattern)
|
||||
pm_.current
|
||||
}
|
||||
if pm_.pattern /= sett.cardTitleTemplate then
|
||||
Just { sett | cardTitleTemplate = pm_.pattern }
|
||||
|
||||
else
|
||||
Nothing
|
||||
@ -384,14 +450,8 @@ update sett msg model =
|
||||
updatePatternModel pm str
|
||||
|
||||
newSettings =
|
||||
if pm_.pattern /= Just sett.cardSubtitleTemplate.pattern then
|
||||
Just
|
||||
{ sett
|
||||
| cardSubtitleTemplate =
|
||||
ItemPattern
|
||||
(Maybe.withDefault "" pm_.pattern)
|
||||
pm_.current
|
||||
}
|
||||
if pm_.pattern /= sett.cardSubtitleTemplate then
|
||||
Just { sett | cardSubtitleTemplate = pm_.pattern }
|
||||
|
||||
else
|
||||
Nothing
|
||||
@ -407,9 +467,21 @@ update sett msg model =
|
||||
not model.searchStatsVisible
|
||||
in
|
||||
( { model | searchStatsVisible = flag }
|
||||
, Just { sett | searchStatsVisible = flag }
|
||||
, Just { sett | searchStatsVisible = Just flag }
|
||||
)
|
||||
|
||||
ToggleAllTabs ->
|
||||
let
|
||||
tabs =
|
||||
if Set.isEmpty model.openTabs then
|
||||
List.map akkordionTabName allTabs
|
||||
|> Set.fromList
|
||||
|
||||
else
|
||||
Set.empty
|
||||
in
|
||||
( { model | openTabs = tabs }, Nothing )
|
||||
|
||||
ToggleAkkordionTab name ->
|
||||
let
|
||||
tabs =
|
||||
@ -429,7 +501,7 @@ update sett msg model =
|
||||
not model.sideMenuVisible
|
||||
in
|
||||
( { model | sideMenuVisible = next }
|
||||
, Just { sett | sideMenuVisible = next }
|
||||
, Just { sett | sideMenuVisible = Just next }
|
||||
)
|
||||
|
||||
TogglePowerSearch ->
|
||||
@ -438,7 +510,7 @@ update sett msg model =
|
||||
not model.powerSearchEnabled
|
||||
in
|
||||
( { model | powerSearchEnabled = next }
|
||||
, Just { sett | powerSearchEnabled = next }
|
||||
, Just { sett | powerSearchEnabled = Just next }
|
||||
)
|
||||
|
||||
UiLangMsg lm ->
|
||||
@ -454,7 +526,7 @@ update sett msg model =
|
||||
Nothing
|
||||
|
||||
else
|
||||
Just { sett | uiLang = newLang }
|
||||
Just { sett | uiLang = Just (Messages.toIso2 newLang) }
|
||||
)
|
||||
|
||||
PdfModeMsg lm ->
|
||||
@ -470,9 +542,53 @@ update sett msg model =
|
||||
Nothing
|
||||
|
||||
else
|
||||
Just { sett | pdfMode = newMode }
|
||||
Just { sett | pdfMode = Just (Data.Pdf.asString newMode) }
|
||||
)
|
||||
|
||||
ResetTab tab ->
|
||||
let
|
||||
newSettings =
|
||||
case tab of
|
||||
GeneralTab ->
|
||||
{ sett | uiLang = Nothing, sideMenuVisible = Nothing }
|
||||
|
||||
SearchTab ->
|
||||
{ sett
|
||||
| itemSearchPageSize = Nothing
|
||||
, searchStatsVisible = Nothing
|
||||
, powerSearchEnabled = Nothing
|
||||
}
|
||||
|
||||
CardsTab ->
|
||||
{ sett
|
||||
| itemSearchNoteLength = Nothing
|
||||
, cardPreviewSize = Nothing
|
||||
, cardTitleTemplate = Nothing
|
||||
, cardSubtitleTemplate = Nothing
|
||||
}
|
||||
|
||||
SearchMenuTab ->
|
||||
{ sett
|
||||
| searchMenuTagCount = Nothing
|
||||
, searchMenuTagCatCount = Nothing
|
||||
, searchMenuFolderCount = Nothing
|
||||
}
|
||||
|
||||
DetailTab ->
|
||||
{ sett | pdfMode = Nothing, itemDetailShortcuts = Nothing }
|
||||
|
||||
TagsTab ->
|
||||
{ sett | tagCategoryColors = Nothing }
|
||||
|
||||
-- no reset here
|
||||
FieldsTab ->
|
||||
{ sett | formFields = Nothing }
|
||||
|
||||
nm =
|
||||
initModel flags newSettings model.defaults
|
||||
in
|
||||
( { nm | openTabs = model.openTabs }, Just newSettings )
|
||||
|
||||
|
||||
|
||||
--- View2
|
||||
@ -495,7 +611,7 @@ tagColorViewOpts2 texts =
|
||||
}
|
||||
|
||||
|
||||
view2 : Texts -> Flags -> UiSettings -> Model -> Html Msg
|
||||
view2 : Texts -> Flags -> StoredUiSettings -> Model -> Html Msg
|
||||
view2 texts flags settings model =
|
||||
let
|
||||
state tab =
|
||||
@ -517,7 +633,7 @@ view2 texts flags settings model =
|
||||
]
|
||||
|
||||
|
||||
settingFormTabs : Texts -> Flags -> UiSettings -> Model -> List (Comp.Tabs.Tab Msg)
|
||||
settingFormTabs : Texts -> Flags -> StoredUiSettings -> Model -> List (Comp.Tabs.Tab Msg)
|
||||
settingFormTabs texts flags _ model =
|
||||
let
|
||||
langCfg =
|
||||
@ -533,10 +649,21 @@ settingFormTabs texts flags _ model =
|
||||
, style = DS.mainStyle
|
||||
, selectPlaceholder = texts.basics.selectPlaceholder
|
||||
}
|
||||
|
||||
resetLink tab =
|
||||
a
|
||||
[ href "#"
|
||||
, class S.link
|
||||
, class "text-sm"
|
||||
, onClick (ResetTab tab)
|
||||
]
|
||||
[ i [ class "fa fa-eraser mr-1" ] []
|
||||
, text "Reset"
|
||||
]
|
||||
in
|
||||
[ { name = "general"
|
||||
[ { name = akkordionTabName GeneralTab
|
||||
, title = texts.general
|
||||
, titleRight = []
|
||||
, titleRight = [ resetLink GeneralTab ]
|
||||
, info = Nothing
|
||||
, body =
|
||||
[ div [ class "mb-4 " ]
|
||||
@ -560,9 +687,9 @@ settingFormTabs texts flags _ model =
|
||||
]
|
||||
]
|
||||
}
|
||||
, { name = "item-search"
|
||||
, { name = akkordionTabName SearchTab
|
||||
, title = texts.itemSearch
|
||||
, titleRight = []
|
||||
, titleRight = [ resetLink SearchTab ]
|
||||
, info = Nothing
|
||||
, body =
|
||||
[ Html.map SearchPageSizeMsg
|
||||
@ -594,9 +721,9 @@ settingFormTabs texts flags _ model =
|
||||
]
|
||||
]
|
||||
}
|
||||
, { name = "item-cards"
|
||||
, { name = akkordionTabName CardsTab
|
||||
, title = texts.itemCards
|
||||
, titleRight = []
|
||||
, titleRight = [ resetLink CardsTab ]
|
||||
, info = Nothing
|
||||
, body =
|
||||
[ Html.map NoteLengthMsg
|
||||
@ -666,9 +793,9 @@ settingFormTabs texts flags _ model =
|
||||
texts.templateHelpMessage
|
||||
]
|
||||
}
|
||||
, { name = "search-menu"
|
||||
, { name = akkordionTabName SearchMenuTab
|
||||
, title = texts.searchMenu
|
||||
, titleRight = []
|
||||
, titleRight = [ resetLink SearchMenuTab ]
|
||||
, info = Nothing
|
||||
, body =
|
||||
[ Html.map SearchMenuTagMsg
|
||||
@ -700,9 +827,9 @@ settingFormTabs texts flags _ model =
|
||||
)
|
||||
]
|
||||
}
|
||||
, { name = "item-detail"
|
||||
, { name = akkordionTabName DetailTab
|
||||
, title = texts.itemDetail
|
||||
, titleRight = []
|
||||
, titleRight = [ resetLink DetailTab ]
|
||||
, info = Nothing
|
||||
, body =
|
||||
[ div [ class "mb-4" ]
|
||||
@ -726,9 +853,9 @@ settingFormTabs texts flags _ model =
|
||||
]
|
||||
]
|
||||
}
|
||||
, { name = "tag-category-colors"
|
||||
, { name = akkordionTabName TagsTab
|
||||
, title = texts.tagCategoryColors
|
||||
, titleRight = []
|
||||
, titleRight = [ resetLink TagsTab ]
|
||||
, info = Nothing
|
||||
, body =
|
||||
[ Html.map TagColorMsg
|
||||
@ -739,9 +866,9 @@ settingFormTabs texts flags _ model =
|
||||
)
|
||||
]
|
||||
}
|
||||
, { name = "fields"
|
||||
, { name = akkordionTabName FieldsTab
|
||||
, title = texts.fields
|
||||
, titleRight = []
|
||||
, titleRight = [ resetLink FieldsTab ]
|
||||
, info = Nothing
|
||||
, body =
|
||||
[ span [ class "opacity-50 text-sm" ]
|
||||
|
@ -16,26 +16,42 @@ module Comp.UiSettingsManage exposing
|
||||
|
||||
import Api
|
||||
import Api.Model.BasicResult exposing (BasicResult)
|
||||
import Comp.Basic
|
||||
import Comp.MenuBar as MB
|
||||
import Comp.UiSettingsForm
|
||||
import Comp.UiSettingsMigrate
|
||||
import Data.AccountScope exposing (AccountScope)
|
||||
import Data.AppEvent exposing (AppEvent(..))
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.UiSettings exposing (StoredUiSettings, UiSettings)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Http
|
||||
import Messages.Comp.UiSettingsManage exposing (Texts)
|
||||
import Page.Search.Data exposing (Msg(..))
|
||||
import Styles as S
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ formModel : Comp.UiSettingsForm.Model
|
||||
, settings : Maybe UiSettings
|
||||
{ formModel : FormView
|
||||
, formResult : FormResult
|
||||
, settingsMigrate : Comp.UiSettingsMigrate.Model
|
||||
, formData : Maybe FormData
|
||||
}
|
||||
|
||||
|
||||
type alias FormData =
|
||||
{ userSettings : StoredUiSettings
|
||||
, userModel : Comp.UiSettingsForm.Model
|
||||
, collSettings : StoredUiSettings
|
||||
, collModel : Comp.UiSettingsForm.Model
|
||||
}
|
||||
|
||||
|
||||
type FormView
|
||||
= ViewLoading
|
||||
| ViewUser
|
||||
| ViewCollective
|
||||
|
||||
|
||||
type FormResult
|
||||
= FormInit
|
||||
| FormUnchanged
|
||||
@ -45,35 +61,39 @@ type FormResult
|
||||
|
||||
|
||||
type Msg
|
||||
= UiSettingsFormMsg Comp.UiSettingsForm.Msg
|
||||
| UiSettingsMigrateMsg Comp.UiSettingsMigrate.Msg
|
||||
= UiFormMsg AccountScope Comp.UiSettingsForm.Msg
|
||||
| Submit
|
||||
| UpdateSettings
|
||||
| SaveSettingsResp UiSettings (Result Http.Error BasicResult)
|
||||
| ReceiveBrowserSettings StoredUiSettings
|
||||
| SaveSettingsResp (Result Http.Error BasicResult)
|
||||
| ReceiveServerSettings (Result Http.Error ( StoredUiSettings, StoredUiSettings ))
|
||||
| ToggleExpandCollapse
|
||||
| SwitchForm AccountScope
|
||||
|
||||
|
||||
init : Flags -> UiSettings -> ( Model, Cmd Msg )
|
||||
init flags settings =
|
||||
let
|
||||
( fm, fc ) =
|
||||
Comp.UiSettingsForm.init flags settings
|
||||
|
||||
( mm, mc ) =
|
||||
Comp.UiSettingsMigrate.init flags
|
||||
in
|
||||
( { formModel = fm
|
||||
, settings = Nothing
|
||||
init : Flags -> ( Model, Cmd Msg )
|
||||
init flags =
|
||||
( { formModel = ViewLoading
|
||||
, formData = Nothing
|
||||
, formResult = FormInit
|
||||
, settingsMigrate = mm
|
||||
}
|
||||
, Cmd.batch
|
||||
[ Cmd.map UiSettingsFormMsg fc
|
||||
, Cmd.map UiSettingsMigrateMsg mc
|
||||
[ Api.getClientSettingsRaw flags ReceiveServerSettings
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
getViewScope : Model -> AccountScope
|
||||
getViewScope model =
|
||||
case model.formModel of
|
||||
ViewCollective ->
|
||||
Data.AccountScope.Collective
|
||||
|
||||
ViewUser ->
|
||||
Data.AccountScope.User
|
||||
|
||||
_ ->
|
||||
Data.AccountScope.User
|
||||
|
||||
|
||||
|
||||
--- update
|
||||
|
||||
@ -82,108 +102,165 @@ type alias UpdateResult =
|
||||
{ model : Model
|
||||
, cmd : Cmd Msg
|
||||
, sub : Sub Msg
|
||||
, newSettings : Maybe UiSettings
|
||||
, appEvent : AppEvent
|
||||
}
|
||||
|
||||
|
||||
unit : Model -> UpdateResult
|
||||
unit model =
|
||||
UpdateResult model Cmd.none Sub.none AppNothing
|
||||
|
||||
|
||||
update : Flags -> UiSettings -> Msg -> Model -> UpdateResult
|
||||
update flags settings msg model =
|
||||
case msg of
|
||||
UiSettingsFormMsg lm ->
|
||||
UiFormMsg scope lm ->
|
||||
case model.formData of
|
||||
Nothing ->
|
||||
unit model
|
||||
|
||||
Just data ->
|
||||
case scope of
|
||||
Data.AccountScope.Collective ->
|
||||
let
|
||||
( m_, sett ) =
|
||||
Comp.UiSettingsForm.update flags data.collSettings lm data.collModel
|
||||
in
|
||||
unit
|
||||
{ model
|
||||
| formData =
|
||||
Just
|
||||
{ data
|
||||
| collSettings = Maybe.withDefault data.collSettings sett
|
||||
, collModel = m_
|
||||
}
|
||||
, formResult =
|
||||
if sett /= Nothing then
|
||||
FormInit
|
||||
|
||||
else
|
||||
model.formResult
|
||||
}
|
||||
|
||||
Data.AccountScope.User ->
|
||||
let
|
||||
( m_, sett ) =
|
||||
Comp.UiSettingsForm.update flags data.userSettings lm data.userModel
|
||||
in
|
||||
unit
|
||||
{ model
|
||||
| formData =
|
||||
Just
|
||||
{ data
|
||||
| userSettings = Maybe.withDefault data.userSettings sett
|
||||
, userModel = m_
|
||||
}
|
||||
, formResult =
|
||||
if sett /= Nothing then
|
||||
FormInit
|
||||
|
||||
else
|
||||
model.formResult
|
||||
}
|
||||
|
||||
Submit ->
|
||||
case ( model.formModel, model.formData ) of
|
||||
( ViewCollective, Just data ) ->
|
||||
{ model = { model | formResult = FormInit }
|
||||
, cmd =
|
||||
Api.saveClientSettings flags
|
||||
data.collSettings
|
||||
Data.AccountScope.Collective
|
||||
SaveSettingsResp
|
||||
, sub = Sub.none
|
||||
, appEvent = AppNothing
|
||||
}
|
||||
|
||||
( ViewUser, Just data ) ->
|
||||
{ model = { model | formResult = FormInit }
|
||||
, cmd =
|
||||
Api.saveClientSettings flags
|
||||
data.userSettings
|
||||
Data.AccountScope.User
|
||||
SaveSettingsResp
|
||||
, sub = Sub.none
|
||||
, appEvent = AppNothing
|
||||
}
|
||||
|
||||
_ ->
|
||||
unit model
|
||||
|
||||
SaveSettingsResp (Ok res) ->
|
||||
case ( res.success, model.formData ) of
|
||||
( True, Just data ) ->
|
||||
let
|
||||
result =
|
||||
update flags
|
||||
settings
|
||||
(ReceiveServerSettings (Ok ( data.collSettings, data.userSettings )))
|
||||
model
|
||||
in
|
||||
{ result | appEvent = AppReloadUiSettings }
|
||||
|
||||
_ ->
|
||||
unit { model | formResult = FormUnknownError }
|
||||
|
||||
SaveSettingsResp (Err err) ->
|
||||
UpdateResult { model | formResult = FormHttpError err } Cmd.none Sub.none AppNothing
|
||||
|
||||
ReceiveServerSettings (Ok ( coll, user )) ->
|
||||
let
|
||||
inSettings =
|
||||
Maybe.withDefault settings model.settings
|
||||
collDefaults =
|
||||
Data.UiSettings.defaults
|
||||
|
||||
( m_, sett ) =
|
||||
Comp.UiSettingsForm.update inSettings lm model.formModel
|
||||
userDefaults =
|
||||
Data.UiSettings.merge coll collDefaults
|
||||
|
||||
( um, uc ) =
|
||||
Comp.UiSettingsForm.init flags user userDefaults
|
||||
|
||||
( cm, cc ) =
|
||||
Comp.UiSettingsForm.init flags coll collDefaults
|
||||
|
||||
model_ =
|
||||
{ model
|
||||
| formData =
|
||||
Just
|
||||
{ userSettings = user
|
||||
, userModel = um
|
||||
, collSettings = coll
|
||||
, collModel = cm
|
||||
}
|
||||
, formModel = ViewUser
|
||||
}
|
||||
|
||||
cmds =
|
||||
Cmd.batch
|
||||
[ Cmd.map (UiFormMsg Data.AccountScope.User) uc
|
||||
, Cmd.map (UiFormMsg Data.AccountScope.Collective) cc
|
||||
]
|
||||
in
|
||||
{ model =
|
||||
{ model
|
||||
| formModel = m_
|
||||
, settings =
|
||||
if sett == Nothing then
|
||||
model.settings
|
||||
UpdateResult model_ cmds Sub.none AppNothing
|
||||
|
||||
else
|
||||
sett
|
||||
, formResult =
|
||||
if sett /= Nothing then
|
||||
FormInit
|
||||
ReceiveServerSettings (Err err) ->
|
||||
unit { model | formResult = FormHttpError err }
|
||||
|
||||
else
|
||||
model.formResult
|
||||
}
|
||||
, cmd = Cmd.none
|
||||
, sub = Sub.none
|
||||
, newSettings = Nothing
|
||||
}
|
||||
|
||||
UiSettingsMigrateMsg lm ->
|
||||
let
|
||||
result =
|
||||
Comp.UiSettingsMigrate.update flags lm model.settingsMigrate
|
||||
in
|
||||
{ model = { model | settingsMigrate = result.model }
|
||||
, cmd = Cmd.map UiSettingsMigrateMsg result.cmd
|
||||
, sub = Sub.map UiSettingsMigrateMsg result.sub
|
||||
, newSettings = result.newSettings
|
||||
}
|
||||
|
||||
ReceiveBrowserSettings sett ->
|
||||
ToggleExpandCollapse ->
|
||||
let
|
||||
lm =
|
||||
UiSettingsMigrateMsg (Comp.UiSettingsMigrate.receiveBrowserSettings sett)
|
||||
UiFormMsg (getViewScope model) Comp.UiSettingsForm.toggleAllTabs
|
||||
in
|
||||
update flags settings lm model
|
||||
|
||||
Submit ->
|
||||
case model.settings of
|
||||
Just s ->
|
||||
{ model = { model | formResult = FormInit }
|
||||
, cmd = Api.saveClientSettings flags s (SaveSettingsResp s)
|
||||
, sub = Sub.none
|
||||
, newSettings = Nothing
|
||||
}
|
||||
|
||||
Nothing ->
|
||||
{ model = { model | formResult = FormUnchanged }
|
||||
, cmd = Cmd.none
|
||||
, sub = Sub.none
|
||||
, newSettings = Nothing
|
||||
}
|
||||
|
||||
SaveSettingsResp newSettings (Ok res) ->
|
||||
if res.success then
|
||||
{ model = { model | formResult = FormSaved }
|
||||
, cmd = Cmd.none
|
||||
, sub = Sub.none
|
||||
, newSettings = Just newSettings
|
||||
}
|
||||
|
||||
else
|
||||
{ model = { model | formResult = FormUnknownError }
|
||||
, cmd = Cmd.none
|
||||
, sub = Sub.none
|
||||
, newSettings = Nothing
|
||||
}
|
||||
|
||||
SaveSettingsResp _ (Err err) ->
|
||||
UpdateResult { model | formResult = FormHttpError err } Cmd.none Sub.none Nothing
|
||||
|
||||
UpdateSettings ->
|
||||
SwitchForm scope ->
|
||||
let
|
||||
( fm, fc ) =
|
||||
Comp.UiSettingsForm.init flags settings
|
||||
forUser =
|
||||
unit { model | formModel = ViewUser }
|
||||
|
||||
forColl =
|
||||
unit { model | formModel = ViewCollective }
|
||||
in
|
||||
{ model = { model | formModel = fm }
|
||||
, cmd = Cmd.map UiSettingsFormMsg fc
|
||||
, sub = Sub.none
|
||||
, newSettings = Nothing
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- View2
|
||||
Data.AccountScope.fold forUser forColl scope
|
||||
|
||||
|
||||
isError : Model -> Bool
|
||||
@ -211,7 +288,11 @@ isSuccess model =
|
||||
|
||||
|
||||
view2 : Texts -> Flags -> UiSettings -> String -> Model -> Html Msg
|
||||
view2 texts flags settings classes model =
|
||||
view2 texts flags _ classes model =
|
||||
let
|
||||
scope =
|
||||
getViewScope model
|
||||
in
|
||||
div [ class classes ]
|
||||
[ MB.view
|
||||
{ start =
|
||||
@ -221,14 +302,29 @@ view2 texts flags settings classes model =
|
||||
, title = texts.saveSettings
|
||||
, icon = Just "fa fa-save"
|
||||
}
|
||||
, MB.SecondaryButton
|
||||
{ tagger = ToggleExpandCollapse
|
||||
, label = ""
|
||||
, title = texts.expandCollapse
|
||||
, icon = Just "fa fa-compress"
|
||||
}
|
||||
]
|
||||
, end =
|
||||
[ MB.RadioButton
|
||||
{ tagger = \_ -> SwitchForm Data.AccountScope.User
|
||||
, label = texts.accountScope Data.AccountScope.User
|
||||
, value = Data.AccountScope.fold True False scope
|
||||
, id = "ui-settings-chooser-user"
|
||||
}
|
||||
, MB.RadioButton
|
||||
{ tagger = \_ -> SwitchForm Data.AccountScope.Collective
|
||||
, label = texts.accountScope Data.AccountScope.Collective
|
||||
, value = Data.AccountScope.fold False True scope
|
||||
, id = "ui-settings-chooser-collective"
|
||||
}
|
||||
]
|
||||
, end = []
|
||||
, rootClasses = "mb-4"
|
||||
}
|
||||
, div []
|
||||
[ Html.map UiSettingsMigrateMsg
|
||||
(Comp.UiSettingsMigrate.view model.settingsMigrate)
|
||||
]
|
||||
, div
|
||||
[ classList
|
||||
[ ( S.successMessage, isSuccess model )
|
||||
@ -252,11 +348,52 @@ view2 texts flags settings classes model =
|
||||
FormUnknownError ->
|
||||
text texts.unknownSaveError
|
||||
]
|
||||
, Html.map UiSettingsFormMsg
|
||||
(Comp.UiSettingsForm.view2
|
||||
texts.uiSettingsForm
|
||||
flags
|
||||
settings
|
||||
model.formModel
|
||||
)
|
||||
, case model.formModel of
|
||||
ViewLoading ->
|
||||
div [ class "h-24 md:relative" ]
|
||||
[ Comp.Basic.loadingDimmer
|
||||
{ label = ""
|
||||
, active = True
|
||||
}
|
||||
]
|
||||
|
||||
ViewCollective ->
|
||||
case model.formData of
|
||||
Just data ->
|
||||
div []
|
||||
[ h2 [ class S.header2 ]
|
||||
[ text texts.collectiveHeader
|
||||
]
|
||||
, Html.map (UiFormMsg scope)
|
||||
(Comp.UiSettingsForm.view2
|
||||
texts.uiSettingsForm
|
||||
flags
|
||||
data.collSettings
|
||||
data.collModel
|
||||
)
|
||||
]
|
||||
|
||||
Nothing ->
|
||||
span [ class "hidden" ] []
|
||||
|
||||
ViewUser ->
|
||||
case model.formData of
|
||||
Just data ->
|
||||
div []
|
||||
[ h2 [ class S.header2 ]
|
||||
[ text texts.userHeader
|
||||
]
|
||||
, div [ class "py-1 opacity-80" ]
|
||||
[ text texts.userInfo
|
||||
]
|
||||
, Html.map (UiFormMsg scope)
|
||||
(Comp.UiSettingsForm.view2 texts.uiSettingsForm
|
||||
flags
|
||||
data.userSettings
|
||||
data.userModel
|
||||
)
|
||||
]
|
||||
|
||||
Nothing ->
|
||||
span [ class "hidden" ] []
|
||||
]
|
||||
|
@ -17,6 +17,7 @@ module Comp.UiSettingsMigrate exposing
|
||||
|
||||
import Api
|
||||
import Api.Model.BasicResult exposing (BasicResult)
|
||||
import Data.AccountScope
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.UiSettings exposing (StoredUiSettings, UiSettings)
|
||||
import Html exposing (..)
|
||||
@ -132,7 +133,10 @@ update flags msg model =
|
||||
Data.UiSettings.merge settings Data.UiSettings.defaults
|
||||
|
||||
cmd =
|
||||
Api.saveClientSettings flags uiSettings (SaveSettingsResp uiSettings)
|
||||
Api.saveClientSettings flags
|
||||
(Data.UiSettings.convert uiSettings)
|
||||
Data.AccountScope.Collective
|
||||
(SaveSettingsResp uiSettings)
|
||||
in
|
||||
{ empty | model = MigrateRequestRunning, cmd = cmd }
|
||||
|
||||
|
@ -23,6 +23,11 @@ fold user coll scope =
|
||||
coll
|
||||
|
||||
|
||||
all : List AccountScope
|
||||
all =
|
||||
[ Collective, User ]
|
||||
|
||||
|
||||
isUser : AccountScope -> Bool
|
||||
isUser scope =
|
||||
fold True False scope
|
||||
|
13
modules/webapp/src/main/elm/Data/AppEvent.elm
Normal file
13
modules/webapp/src/main/elm/Data/AppEvent.elm
Normal file
@ -0,0 +1,13 @@
|
||||
{-
|
||||
Copyright 2020 Eike K. & Contributors
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-}
|
||||
|
||||
|
||||
module Data.AppEvent exposing (AppEvent(..))
|
||||
|
||||
|
||||
type AppEvent
|
||||
= AppNothing
|
||||
| AppReloadUiSettings
|
@ -15,8 +15,10 @@ module Data.UiSettings exposing
|
||||
, catColor
|
||||
, catColorFg2
|
||||
, catColorString2
|
||||
, convert
|
||||
, defaults
|
||||
, documentationSite
|
||||
, emptyStoredSettings
|
||||
, fieldHidden
|
||||
, fieldVisible
|
||||
, getUiLanguage
|
||||
@ -30,7 +32,6 @@ module Data.UiSettings exposing
|
||||
, tagColor
|
||||
, tagColorFg2
|
||||
, tagColorString2
|
||||
, toStoredUiSettings
|
||||
)
|
||||
|
||||
import Api.Model.Tag exposing (Tag)
|
||||
@ -62,7 +63,7 @@ force default settings.
|
||||
-}
|
||||
type alias StoredUiSettings =
|
||||
{ itemSearchPageSize : Maybe Int
|
||||
, tagCategoryColors : List ( String, String )
|
||||
, tagCategoryColors : Maybe (List ( String, String ))
|
||||
, pdfMode : Maybe String
|
||||
, itemSearchNoteLength : Maybe Int
|
||||
, itemDetailNotesPosition : Maybe String
|
||||
@ -70,23 +71,51 @@ type alias StoredUiSettings =
|
||||
, searchMenuTagCount : Maybe Int
|
||||
, searchMenuTagCatCount : Maybe Int
|
||||
, formFields : Maybe (List String)
|
||||
, itemDetailShortcuts : Bool
|
||||
, searchMenuVisible : Bool
|
||||
, editMenuVisible : Bool
|
||||
, itemDetailShortcuts : Maybe Bool
|
||||
, searchMenuVisible : Maybe Bool
|
||||
, editMenuVisible : Maybe Bool
|
||||
, cardPreviewSize : Maybe String
|
||||
, cardTitleTemplate : Maybe String
|
||||
, cardSubtitleTemplate : Maybe String
|
||||
, searchStatsVisible : Bool
|
||||
, cardPreviewFullWidth : Bool
|
||||
, searchStatsVisible : Maybe Bool
|
||||
, cardPreviewFullWidth : Maybe Bool
|
||||
, uiTheme : Maybe String
|
||||
, sideMenuVisible : Bool
|
||||
, powerSearchEnabled : Bool
|
||||
, sideMenuVisible : Maybe Bool
|
||||
, powerSearchEnabled : Maybe Bool
|
||||
, uiLang : Maybe String
|
||||
, itemSearchShowGroups : Bool
|
||||
, itemSearchShowGroups : Maybe Bool
|
||||
, itemSearchArrange : Maybe String
|
||||
}
|
||||
|
||||
|
||||
emptyStoredSettings : StoredUiSettings
|
||||
emptyStoredSettings =
|
||||
{ itemSearchPageSize = Nothing
|
||||
, tagCategoryColors = Nothing
|
||||
, pdfMode = Nothing
|
||||
, itemSearchNoteLength = Nothing
|
||||
, itemDetailNotesPosition = Nothing
|
||||
, searchMenuFolderCount = Nothing
|
||||
, searchMenuTagCount = Nothing
|
||||
, searchMenuTagCatCount = Nothing
|
||||
, formFields = Nothing
|
||||
, itemDetailShortcuts = Nothing
|
||||
, searchMenuVisible = Nothing
|
||||
, editMenuVisible = Nothing
|
||||
, cardPreviewSize = Nothing
|
||||
, cardTitleTemplate = Nothing
|
||||
, cardSubtitleTemplate = Nothing
|
||||
, searchStatsVisible = Nothing
|
||||
, cardPreviewFullWidth = Nothing
|
||||
, uiTheme = Nothing
|
||||
, sideMenuVisible = Nothing
|
||||
, powerSearchEnabled = Nothing
|
||||
, uiLang = Nothing
|
||||
, itemSearchShowGroups = Nothing
|
||||
, itemSearchArrange = Nothing
|
||||
}
|
||||
|
||||
|
||||
storedUiSettingsDecoder : Decode.Decoder StoredUiSettings
|
||||
storedUiSettingsDecoder =
|
||||
let
|
||||
@ -96,12 +125,12 @@ storedUiSettingsDecoder =
|
||||
maybeString =
|
||||
Decode.maybe Decode.string
|
||||
|
||||
def =
|
||||
defaults
|
||||
maybeBool =
|
||||
Decode.maybe Decode.bool
|
||||
in
|
||||
Decode.succeed StoredUiSettings
|
||||
|> P.optional "itemSearchPageSize" maybeInt Nothing
|
||||
|> P.optional "tagCategoryColors" (Decode.keyValuePairs Decode.string) []
|
||||
|> P.optional "tagCategoryColors" (Decode.maybe <| Decode.keyValuePairs Decode.string) Nothing
|
||||
|> P.optional "pdfMode" maybeString Nothing
|
||||
|> P.optional "itemSearchNoteLength" maybeInt Nothing
|
||||
|> P.optional "itemDetailNotesPosition" maybeString Nothing
|
||||
@ -109,53 +138,56 @@ storedUiSettingsDecoder =
|
||||
|> P.optional "searchMenuTagCount" maybeInt Nothing
|
||||
|> P.optional "searchMenuTagCatCount" maybeInt Nothing
|
||||
|> P.optional "formFields" (Decode.maybe <| Decode.list Decode.string) Nothing
|
||||
|> P.optional "itemDetailShortcuts" Decode.bool def.itemDetailShortcuts
|
||||
|> P.optional "searchMenuVisible" Decode.bool def.searchMenuVisible
|
||||
|> P.optional "editMenuVisible" Decode.bool def.editMenuVisible
|
||||
|> P.optional "itemDetailShortcuts" maybeBool Nothing
|
||||
|> P.optional "searchMenuVisible" maybeBool Nothing
|
||||
|> P.optional "editMenuVisible" maybeBool Nothing
|
||||
|> P.optional "cardPreviewSize" maybeString Nothing
|
||||
|> P.optional "cardTitleTemplate" maybeString Nothing
|
||||
|> P.optional "cardSubtitleTemplate" maybeString Nothing
|
||||
|> P.optional "searchStatsVisible" Decode.bool def.searchStatsVisible
|
||||
|> P.optional "cardPreviewFullWidth" Decode.bool def.cardPreviewFullWidth
|
||||
|> P.optional "searchStatsVisible" maybeBool Nothing
|
||||
|> P.optional "cardPreviewFullWidth" maybeBool Nothing
|
||||
|> P.optional "uiTheme" maybeString Nothing
|
||||
|> P.optional "sideMenuVisible" Decode.bool def.sideMenuVisible
|
||||
|> P.optional "powerSearchEnabled" Decode.bool def.powerSearchEnabled
|
||||
|> P.optional "sideMenuVisible" maybeBool Nothing
|
||||
|> P.optional "powerSearchEnabled" maybeBool Nothing
|
||||
|> P.optional "uiLang" maybeString Nothing
|
||||
|> P.optional "itemSearchShowGroups" Decode.bool def.itemSearchShowGroups
|
||||
|> P.optional "itemSearchShowGroups" maybeBool Nothing
|
||||
|> P.optional "itemSearchArrange" maybeString Nothing
|
||||
|
||||
|
||||
storedUiSettingsEncode : StoredUiSettings -> Encode.Value
|
||||
storedUiSettingsEncode value =
|
||||
let
|
||||
maybeEnc enca ma =
|
||||
Maybe.map enca ma |> Maybe.withDefault Encode.null
|
||||
maybeEnc field enca ma =
|
||||
Maybe.map (\a -> ( field, enca a )) ma
|
||||
in
|
||||
Encode.object
|
||||
[ ( "itemSearchPageSize", maybeEnc Encode.int value.itemSearchPageSize )
|
||||
, ( "tagCategoryColors", Encode.dict identity Encode.string (Dict.fromList value.tagCategoryColors) )
|
||||
, ( "pdfMode", maybeEnc Encode.string value.pdfMode )
|
||||
, ( "itemSearchNoteLength", maybeEnc Encode.int value.itemSearchNoteLength )
|
||||
, ( "itemDetailNotesPosition", maybeEnc Encode.string value.itemDetailNotesPosition )
|
||||
, ( "searchMenuFolderCount", maybeEnc Encode.int value.searchMenuFolderCount )
|
||||
, ( "searchMenuTagCount", maybeEnc Encode.int value.searchMenuTagCount )
|
||||
, ( "searchMenuTagCatCount", maybeEnc Encode.int value.searchMenuTagCatCount )
|
||||
, ( "formFields", maybeEnc (Encode.list Encode.string) value.formFields )
|
||||
, ( "itemDetailShortcuts", Encode.bool value.itemDetailShortcuts )
|
||||
, ( "searchMenuVisible", Encode.bool value.searchMenuVisible )
|
||||
, ( "editMenuVisible", Encode.bool value.editMenuVisible )
|
||||
, ( "cardPreviewSize", maybeEnc Encode.string value.cardPreviewSize )
|
||||
, ( "cardTitleTemplate", maybeEnc Encode.string value.cardTitleTemplate )
|
||||
, ( "cardSubtitleTemplate", maybeEnc Encode.string value.cardSubtitleTemplate )
|
||||
, ( "searchStatsVisible", Encode.bool value.searchStatsVisible )
|
||||
, ( "cardPreviewFullWidth", Encode.bool value.cardPreviewFullWidth )
|
||||
, ( "uiTheme", maybeEnc Encode.string value.uiTheme )
|
||||
, ( "sideMenuVisible", Encode.bool value.sideMenuVisible )
|
||||
, ( "powerSearchEnabled", Encode.bool value.powerSearchEnabled )
|
||||
, ( "uiLang", maybeEnc Encode.string value.uiLang )
|
||||
, ( "itemSearchShowGroups", Encode.bool value.itemSearchShowGroups )
|
||||
, ( "itemSearchArrange", maybeEnc Encode.string value.itemSearchArrange )
|
||||
]
|
||||
Encode.object <|
|
||||
List.filterMap identity
|
||||
[ maybeEnc "itemSearchPageSize" Encode.int value.itemSearchPageSize
|
||||
, maybeEnc "tagCategoryColors"
|
||||
(Encode.dict identity Encode.string)
|
||||
(Maybe.map Dict.fromList value.tagCategoryColors)
|
||||
, maybeEnc "pdfMode" Encode.string value.pdfMode
|
||||
, maybeEnc "itemSearchNoteLength" Encode.int value.itemSearchNoteLength
|
||||
, maybeEnc "itemDetailNotesPosition" Encode.string value.itemDetailNotesPosition
|
||||
, maybeEnc "searchMenuFolderCount" Encode.int value.searchMenuFolderCount
|
||||
, maybeEnc "searchMenuTagCount" Encode.int value.searchMenuTagCount
|
||||
, maybeEnc "searchMenuTagCatCount" Encode.int value.searchMenuTagCatCount
|
||||
, maybeEnc "formFields" (Encode.list Encode.string) value.formFields
|
||||
, maybeEnc "itemDetailShortcuts" Encode.bool value.itemDetailShortcuts
|
||||
, maybeEnc "searchMenuVisible" Encode.bool value.searchMenuVisible
|
||||
, maybeEnc "editMenuVisible" Encode.bool value.editMenuVisible
|
||||
, maybeEnc "cardPreviewSize" Encode.string value.cardPreviewSize
|
||||
, maybeEnc "cardTitleTemplate" Encode.string value.cardTitleTemplate
|
||||
, maybeEnc "cardSubtitleTemplate" Encode.string value.cardSubtitleTemplate
|
||||
, maybeEnc "searchStatsVisible" Encode.bool value.searchStatsVisible
|
||||
, maybeEnc "cardPreviewFullWidth" Encode.bool value.cardPreviewFullWidth
|
||||
, maybeEnc "uiTheme" Encode.string value.uiTheme
|
||||
, maybeEnc "sideMenuVisible" Encode.bool value.sideMenuVisible
|
||||
, maybeEnc "powerSearchEnabled" Encode.bool value.powerSearchEnabled
|
||||
, maybeEnc "uiLang" Encode.string value.uiLang
|
||||
, maybeEnc "itemSearchShowGroups" Encode.bool value.itemSearchShowGroups
|
||||
, maybeEnc "itemSearchArrange" Encode.string value.itemSearchArrange
|
||||
]
|
||||
|
||||
|
||||
{-| Settings for the web ui. These fields are all mandatory, since
|
||||
@ -273,7 +305,8 @@ merge given fallback =
|
||||
choose given.itemSearchPageSize fallback.itemSearchPageSize
|
||||
, tagCategoryColors =
|
||||
Dict.union
|
||||
(Dict.fromList given.tagCategoryColors
|
||||
(Maybe.map Dict.fromList given.tagCategoryColors
|
||||
|> Maybe.withDefault Dict.empty
|
||||
|> Dict.map (\_ -> Data.Color.fromString)
|
||||
|> Dict.filter (\_ -> \mc -> mc /= Nothing)
|
||||
|> Dict.map (\_ -> Maybe.withDefault Data.Color.Grey)
|
||||
@ -299,9 +332,9 @@ merge given fallback =
|
||||
choose
|
||||
(Maybe.map Data.Fields.fromList given.formFields)
|
||||
fallback.formFields
|
||||
, itemDetailShortcuts = given.itemDetailShortcuts
|
||||
, searchMenuVisible = given.searchMenuVisible
|
||||
, editMenuVisible = given.editMenuVisible
|
||||
, itemDetailShortcuts = choose given.itemDetailShortcuts fallback.itemDetailShortcuts
|
||||
, searchMenuVisible = choose given.searchMenuVisible fallback.searchMenuVisible
|
||||
, editMenuVisible = choose given.editMenuVisible fallback.editMenuVisible
|
||||
, cardPreviewSize =
|
||||
given.cardPreviewSize
|
||||
|> Maybe.andThen Data.BasicSize.fromString
|
||||
@ -312,17 +345,17 @@ merge given fallback =
|
||||
, cardSubtitleTemplate =
|
||||
Maybe.andThen readPattern given.cardSubtitleTemplate
|
||||
|> Maybe.withDefault fallback.cardSubtitleTemplate
|
||||
, searchStatsVisible = given.searchStatsVisible
|
||||
, cardPreviewFullWidth = given.cardPreviewFullWidth
|
||||
, searchStatsVisible = choose given.searchStatsVisible fallback.searchStatsVisible
|
||||
, cardPreviewFullWidth = choose given.cardPreviewFullWidth fallback.cardPreviewFullWidth
|
||||
, uiTheme =
|
||||
Maybe.andThen Data.UiTheme.fromString given.uiTheme
|
||||
|> Maybe.withDefault fallback.uiTheme
|
||||
, sideMenuVisible = given.sideMenuVisible
|
||||
, powerSearchEnabled = given.powerSearchEnabled
|
||||
, sideMenuVisible = choose given.sideMenuVisible fallback.sideMenuVisible
|
||||
, powerSearchEnabled = choose given.powerSearchEnabled fallback.powerSearchEnabled
|
||||
, uiLang =
|
||||
Maybe.map Messages.fromIso2 given.uiLang
|
||||
|> Maybe.withDefault Messages.UiLanguage.English
|
||||
, itemSearchShowGroups = given.itemSearchShowGroups
|
||||
|> Maybe.withDefault fallback.uiLang
|
||||
, itemSearchShowGroups = choose given.itemSearchShowGroups fallback.itemSearchShowGroups
|
||||
, itemSearchArrange =
|
||||
Maybe.andThen Data.ItemArrange.fromString given.itemSearchArrange
|
||||
|> Maybe.withDefault fallback.itemSearchArrange
|
||||
@ -334,12 +367,13 @@ mergeDefaults given =
|
||||
merge given defaults
|
||||
|
||||
|
||||
toStoredUiSettings : UiSettings -> StoredUiSettings
|
||||
toStoredUiSettings settings =
|
||||
convert : UiSettings -> StoredUiSettings
|
||||
convert settings =
|
||||
{ itemSearchPageSize = Just settings.itemSearchPageSize
|
||||
, tagCategoryColors =
|
||||
Dict.map (\_ -> Data.Color.toString) settings.tagCategoryColors
|
||||
|> Dict.toList
|
||||
|> Just
|
||||
, pdfMode = Just (Data.Pdf.asString settings.pdfMode)
|
||||
, itemSearchNoteLength = Just settings.itemSearchNoteLength
|
||||
, itemDetailNotesPosition = Just (posToString settings.itemDetailNotesPosition)
|
||||
@ -349,22 +383,22 @@ toStoredUiSettings settings =
|
||||
, formFields =
|
||||
List.map Data.Fields.toString settings.formFields
|
||||
|> Just
|
||||
, itemDetailShortcuts = settings.itemDetailShortcuts
|
||||
, searchMenuVisible = settings.searchMenuVisible
|
||||
, editMenuVisible = settings.editMenuVisible
|
||||
, itemDetailShortcuts = Just settings.itemDetailShortcuts
|
||||
, searchMenuVisible = Just settings.searchMenuVisible
|
||||
, editMenuVisible = Just settings.editMenuVisible
|
||||
, cardPreviewSize =
|
||||
settings.cardPreviewSize
|
||||
|> Data.BasicSize.asString
|
||||
|> Just
|
||||
, cardTitleTemplate = settings.cardTitleTemplate.pattern |> Just
|
||||
, cardSubtitleTemplate = settings.cardSubtitleTemplate.pattern |> Just
|
||||
, searchStatsVisible = settings.searchStatsVisible
|
||||
, cardPreviewFullWidth = settings.cardPreviewFullWidth
|
||||
, searchStatsVisible = Just settings.searchStatsVisible
|
||||
, cardPreviewFullWidth = Just settings.cardPreviewFullWidth
|
||||
, uiTheme = Just (Data.UiTheme.toString settings.uiTheme)
|
||||
, sideMenuVisible = settings.sideMenuVisible
|
||||
, powerSearchEnabled = settings.powerSearchEnabled
|
||||
, sideMenuVisible = Just settings.sideMenuVisible
|
||||
, powerSearchEnabled = Just settings.powerSearchEnabled
|
||||
, uiLang = Just <| Messages.toIso2 settings.uiLang
|
||||
, itemSearchShowGroups = settings.itemSearchShowGroups
|
||||
, itemSearchShowGroups = Just settings.itemSearchShowGroups
|
||||
, itemSearchArrange = Data.ItemArrange.asString settings.itemSearchArrange |> Just
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,6 @@ import App.View2
|
||||
import Browser exposing (Document)
|
||||
import Browser.Navigation exposing (Key)
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.NotificationChannel
|
||||
import Data.UiSettings
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
@ -93,6 +92,5 @@ subscriptions : Model -> Sub Msg
|
||||
subscriptions model =
|
||||
Sub.batch
|
||||
[ model.subs
|
||||
, Ports.receiveUiSettings ReceiveBrowserSettings
|
||||
, Ports.receiveServerEvent ReceiveWsMessage
|
||||
]
|
||||
|
@ -15,16 +15,22 @@ import Http
|
||||
import Messages.Basics
|
||||
import Messages.Comp.HttpError
|
||||
import Messages.Comp.UiSettingsForm
|
||||
import Messages.Data.AccountScope
|
||||
|
||||
|
||||
type alias Texts =
|
||||
{ basics : Messages.Basics.Texts
|
||||
, uiSettingsForm : Messages.Comp.UiSettingsForm.Texts
|
||||
, accountScope : Messages.Data.AccountScope.Texts
|
||||
, saveSettings : String
|
||||
, settingsUnchanged : String
|
||||
, settingsSaved : String
|
||||
, unknownSaveError : String
|
||||
, httpError : Http.Error -> String
|
||||
, userHeader : String
|
||||
, userInfo : String
|
||||
, collectiveHeader : String
|
||||
, expandCollapse : String
|
||||
}
|
||||
|
||||
|
||||
@ -32,11 +38,16 @@ gb : Texts
|
||||
gb =
|
||||
{ basics = Messages.Basics.gb
|
||||
, uiSettingsForm = Messages.Comp.UiSettingsForm.gb
|
||||
, accountScope = Messages.Data.AccountScope.gb
|
||||
, saveSettings = "Save settings"
|
||||
, settingsUnchanged = "Settings unchanged or invalid."
|
||||
, settingsSaved = "Settings saved."
|
||||
, unknownSaveError = "Unknown error while trying to save settings."
|
||||
, httpError = Messages.Comp.HttpError.gb
|
||||
, userHeader = "Personal settings"
|
||||
, userInfo = "Your personal settings override those of the collective. On reset, settings are set back to those of the collective."
|
||||
, collectiveHeader = "Collective settings"
|
||||
, expandCollapse = "Expand/collapse all"
|
||||
}
|
||||
|
||||
|
||||
@ -44,9 +55,14 @@ de : Texts
|
||||
de =
|
||||
{ basics = Messages.Basics.de
|
||||
, uiSettingsForm = Messages.Comp.UiSettingsForm.de
|
||||
, accountScope = Messages.Data.AccountScope.de
|
||||
, saveSettings = "Einstellungen speichern"
|
||||
, settingsUnchanged = "Einstellungen nicht verändert oder ungültig."
|
||||
, settingsSaved = "Einstellungen gespeichert"
|
||||
, unknownSaveError = "Unbekannter Fehler beim Speichern der Einstellungen."
|
||||
, httpError = Messages.Comp.HttpError.de
|
||||
, userHeader = "Persönliche Einstellungen"
|
||||
, userInfo = "Die persönlichen Einstellungen überschreiben die des Kollektivs. Wenn Einstellungen zurückgesetzt werden, werden sie auf die Werte des Kollektivs gesetzt."
|
||||
, collectiveHeader = "Kollektiv Einstellungen"
|
||||
, expandCollapse = "Alle ein-/ausklappen"
|
||||
}
|
||||
|
@ -80,8 +80,7 @@ gb =
|
||||
, changePassword = "Change Password"
|
||||
, channelSettings = "Notification Channels"
|
||||
, uiSettingsInfo =
|
||||
"These settings only affect the web ui. They are stored in the browser, "
|
||||
++ "so they are separated between browsers and devices."
|
||||
"These settings only affect the web ui. Settings can be stored to the collective or to your personal user. Personal settings are prefered when both values exist."
|
||||
, scanMailboxInfo1 =
|
||||
"Docspell can scan folders of your mailbox to import your mails. "
|
||||
++ "You need to provide a connection in "
|
||||
@ -144,7 +143,7 @@ de =
|
||||
, channelSettings = "Benachrichtigungskanäle"
|
||||
, changePassword = "Passwort ändern"
|
||||
, uiSettingsInfo =
|
||||
"Diese Einstellungen sind für die Web-Oberfläche."
|
||||
"Diese Einstellungen sind für die Web-Oberfläche. Es kann entweder für das ganze Kollektiv Einstellungen gemacht werden oder persönliche. Die persönlichen Einstellungen werden bevorzugt, falls beide gesetzt sind."
|
||||
, scanMailboxInfo1 =
|
||||
"""Docspell kann Postfächer durchsuchen und E-Mails importieren. Dafür sind
|
||||
E-Mail-Einstellungen (IMAP) notwendig."""
|
||||
|
@ -231,7 +231,7 @@ type Msg
|
||||
| KeyUpPowerSearchbarMsg (Maybe KeyCode)
|
||||
| RequestReprocessSelected
|
||||
| ReprocessSelectedConfirmed
|
||||
| ClientSettingsSaveResp UiSettings (Result Http.Error BasicResult)
|
||||
| ClientSettingsSaveResp (Result Http.Error BasicResult)
|
||||
| RemoveItem String
|
||||
| MergeSelectedItems
|
||||
| MergeItemsMsg Comp.ItemMerge.Msg
|
||||
|
@ -22,7 +22,9 @@ import Comp.LinkTarget exposing (LinkTarget)
|
||||
import Comp.PowerSearchInput
|
||||
import Comp.PublishItems
|
||||
import Comp.SearchMenu
|
||||
import Data.AppEvent exposing (AppEvent(..))
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.ItemArrange
|
||||
import Data.ItemQuery as Q
|
||||
import Data.ItemSelection
|
||||
import Data.Items
|
||||
@ -44,7 +46,7 @@ type alias UpdateResult =
|
||||
{ model : Model
|
||||
, cmd : Cmd Msg
|
||||
, sub : Sub Msg
|
||||
, newSettings : Maybe UiSettings
|
||||
, appEvent : AppEvent
|
||||
}
|
||||
|
||||
|
||||
@ -74,7 +76,7 @@ update bookmarkId mId key flags texts settings msg model =
|
||||
model
|
||||
|
||||
DoNothing ->
|
||||
UpdateResult model Cmd.none Sub.none Nothing
|
||||
UpdateResult model Cmd.none Sub.none AppNothing
|
||||
|
||||
ResetSearch ->
|
||||
let
|
||||
@ -868,26 +870,26 @@ update bookmarkId mId key flags texts settings msg model =
|
||||
|
||||
TogglePreviewFullWidth ->
|
||||
let
|
||||
newSettings =
|
||||
{ settings | cardPreviewFullWidth = not settings.cardPreviewFullWidth }
|
||||
newSettings s =
|
||||
{ s | cardPreviewFullWidth = Just (not settings.cardPreviewFullWidth) }
|
||||
|
||||
cmd =
|
||||
Api.saveClientSettings flags newSettings (ClientSettingsSaveResp newSettings)
|
||||
Api.saveUserClientSettingsBy flags newSettings ClientSettingsSaveResp
|
||||
in
|
||||
noSub ( { model | viewMenuOpen = False }, cmd )
|
||||
|
||||
ClientSettingsSaveResp newSettings (Ok res) ->
|
||||
ClientSettingsSaveResp (Ok res) ->
|
||||
if res.success then
|
||||
{ model = model
|
||||
, cmd = Cmd.none
|
||||
, sub = Sub.none
|
||||
, newSettings = Just newSettings
|
||||
, appEvent = AppReloadUiSettings
|
||||
}
|
||||
|
||||
else
|
||||
noSub ( model, Cmd.none )
|
||||
|
||||
ClientSettingsSaveResp _ (Err _) ->
|
||||
ClientSettingsSaveResp (Err _) ->
|
||||
noSub ( model, Cmd.none )
|
||||
|
||||
PowerSearchMsg lm ->
|
||||
@ -1015,21 +1017,21 @@ update bookmarkId mId key flags texts settings msg model =
|
||||
|
||||
ToggleShowGroups ->
|
||||
let
|
||||
newSettings =
|
||||
{ settings | itemSearchShowGroups = not settings.itemSearchShowGroups }
|
||||
newSettings s =
|
||||
{ s | itemSearchShowGroups = Just (not settings.itemSearchShowGroups) }
|
||||
|
||||
cmd =
|
||||
Api.saveClientSettings flags newSettings (ClientSettingsSaveResp newSettings)
|
||||
Api.saveUserClientSettingsBy flags newSettings ClientSettingsSaveResp
|
||||
in
|
||||
noSub ( { model | viewMenuOpen = False }, cmd )
|
||||
|
||||
ToggleArrange am ->
|
||||
let
|
||||
newSettings =
|
||||
{ settings | itemSearchArrange = am }
|
||||
newSettings s =
|
||||
{ s | itemSearchArrange = Data.ItemArrange.asString am |> Just }
|
||||
|
||||
cmd =
|
||||
Api.saveClientSettings flags newSettings (ClientSettingsSaveResp newSettings)
|
||||
Api.saveUserClientSettingsBy flags newSettings ClientSettingsSaveResp
|
||||
in
|
||||
noSub ( { model | viewMenuOpen = False }, cmd )
|
||||
|
||||
@ -1201,5 +1203,5 @@ makeResult ( m, c, s ) =
|
||||
{ model = m
|
||||
, cmd = c
|
||||
, sub = s
|
||||
, newSettings = Nothing
|
||||
, appEvent = AppNothing
|
||||
}
|
||||
|
@ -42,10 +42,10 @@ type alias Model =
|
||||
|
||||
|
||||
init : Flags -> UiSettings -> ( Model, Cmd Msg )
|
||||
init flags settings =
|
||||
init flags _ =
|
||||
let
|
||||
( um, uc ) =
|
||||
Comp.UiSettingsManage.init flags settings
|
||||
Comp.UiSettingsManage.init flags
|
||||
|
||||
( otpm, otpc ) =
|
||||
Comp.OtpSetup.init flags
|
||||
@ -107,5 +107,3 @@ type Msg
|
||||
| NotificationHookMsg Comp.NotificationHookManage.Msg
|
||||
| PeriodicQueryMsg Comp.PeriodicQueryTaskManage.Msg
|
||||
| ChannelMsg Comp.NotificationChannelManage.Msg
|
||||
| UpdateSettings
|
||||
| ReceiveBrowserSettings StoredUiSettings
|
||||
|
@ -17,6 +17,7 @@ import Comp.OtpSetup
|
||||
import Comp.PeriodicQueryTaskManage
|
||||
import Comp.ScanMailboxManage
|
||||
import Comp.UiSettingsManage
|
||||
import Data.AppEvent exposing (AppEvent(..))
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
import Page.UserSettings.Data exposing (..)
|
||||
@ -26,10 +27,15 @@ type alias UpdateResult =
|
||||
{ model : Model
|
||||
, cmd : Cmd Msg
|
||||
, sub : Sub Msg
|
||||
, newSettings : Maybe UiSettings
|
||||
, appEvent : AppEvent
|
||||
}
|
||||
|
||||
|
||||
unit : Model -> UpdateResult
|
||||
unit model =
|
||||
UpdateResult model Cmd.none Sub.none AppNothing
|
||||
|
||||
|
||||
update : Flags -> UiSettings -> Msg -> Model -> UpdateResult
|
||||
update flags settings msg model =
|
||||
case msg of
|
||||
@ -47,7 +53,7 @@ update flags settings msg model =
|
||||
{ model = { m | emailSettingsModel = em }
|
||||
, cmd = Cmd.map EmailSettingsMsg c
|
||||
, sub = Sub.none
|
||||
, newSettings = Nothing
|
||||
, appEvent = AppNothing
|
||||
}
|
||||
|
||||
ImapSettingsTab ->
|
||||
@ -58,18 +64,14 @@ update flags settings msg model =
|
||||
{ model = { m | imapSettingsModel = em }
|
||||
, cmd = Cmd.map ImapSettingsMsg c
|
||||
, sub = Sub.none
|
||||
, newSettings = Nothing
|
||||
, appEvent = AppNothing
|
||||
}
|
||||
|
||||
ChangePassTab ->
|
||||
UpdateResult m Cmd.none Sub.none Nothing
|
||||
unit m
|
||||
|
||||
NotificationTab ->
|
||||
{ model = m
|
||||
, cmd = Cmd.none
|
||||
, sub = Sub.none
|
||||
, newSettings = Nothing
|
||||
}
|
||||
unit m
|
||||
|
||||
NotificationWebhookTab ->
|
||||
let
|
||||
@ -79,7 +81,7 @@ update flags settings msg model =
|
||||
{ model = m
|
||||
, cmd = Cmd.map NotificationHookMsg nc
|
||||
, sub = Sub.none
|
||||
, newSettings = Nothing
|
||||
, appEvent = AppNothing
|
||||
}
|
||||
|
||||
NotificationQueriesTab ->
|
||||
@ -88,7 +90,7 @@ update flags settings msg model =
|
||||
Cmd.map NotificationMsg
|
||||
(Tuple.second (Comp.DueItemsTaskManage.init flags))
|
||||
in
|
||||
UpdateResult m initCmd Sub.none Nothing
|
||||
UpdateResult m initCmd Sub.none AppNothing
|
||||
|
||||
NotificationDueItemsTab ->
|
||||
let
|
||||
@ -96,7 +98,7 @@ update flags settings msg model =
|
||||
Cmd.map NotificationMsg
|
||||
(Tuple.second (Comp.DueItemsTaskManage.init flags))
|
||||
in
|
||||
UpdateResult m initCmd Sub.none Nothing
|
||||
UpdateResult m initCmd Sub.none AppNothing
|
||||
|
||||
ScanMailboxTab ->
|
||||
let
|
||||
@ -104,16 +106,24 @@ update flags settings msg model =
|
||||
Cmd.map ScanMailboxMsg
|
||||
(Tuple.second (Comp.ScanMailboxManage.init flags))
|
||||
in
|
||||
UpdateResult m initCmd Sub.none Nothing
|
||||
UpdateResult m initCmd Sub.none AppNothing
|
||||
|
||||
UiSettingsTab ->
|
||||
UpdateResult m Cmd.none Sub.none Nothing
|
||||
let
|
||||
( um, uc ) =
|
||||
Comp.UiSettingsManage.init flags
|
||||
in
|
||||
{ model = { m | uiSettingsModel = um }
|
||||
, cmd = Cmd.map UiSettingsMsg uc
|
||||
, sub = Sub.none
|
||||
, appEvent = AppNothing
|
||||
}
|
||||
|
||||
OtpTab ->
|
||||
UpdateResult m Cmd.none Sub.none Nothing
|
||||
unit m
|
||||
|
||||
ChannelTab ->
|
||||
UpdateResult m Cmd.none Sub.none Nothing
|
||||
unit m
|
||||
|
||||
ChangePassMsg m ->
|
||||
let
|
||||
@ -123,7 +133,7 @@ update flags settings msg model =
|
||||
{ model = { model | changePassModel = m2 }
|
||||
, cmd = Cmd.map ChangePassMsg c2
|
||||
, sub = Sub.none
|
||||
, newSettings = Nothing
|
||||
, appEvent = AppNothing
|
||||
}
|
||||
|
||||
EmailSettingsMsg m ->
|
||||
@ -134,7 +144,7 @@ update flags settings msg model =
|
||||
{ model = { model | emailSettingsModel = m2 }
|
||||
, cmd = Cmd.map EmailSettingsMsg c2
|
||||
, sub = Sub.none
|
||||
, newSettings = Nothing
|
||||
, appEvent = AppNothing
|
||||
}
|
||||
|
||||
ImapSettingsMsg m ->
|
||||
@ -145,7 +155,7 @@ update flags settings msg model =
|
||||
{ model = { model | imapSettingsModel = m2 }
|
||||
, cmd = Cmd.map ImapSettingsMsg c2
|
||||
, sub = Sub.none
|
||||
, newSettings = Nothing
|
||||
, appEvent = AppNothing
|
||||
}
|
||||
|
||||
NotificationMsg lm ->
|
||||
@ -156,7 +166,7 @@ update flags settings msg model =
|
||||
{ model = { model | notificationModel = m2 }
|
||||
, cmd = Cmd.map NotificationMsg c2
|
||||
, sub = Sub.none
|
||||
, newSettings = Nothing
|
||||
, appEvent = AppNothing
|
||||
}
|
||||
|
||||
ScanMailboxMsg lm ->
|
||||
@ -167,7 +177,7 @@ update flags settings msg model =
|
||||
{ model = { model | scanMailboxModel = m2 }
|
||||
, cmd = Cmd.map ScanMailboxMsg c2
|
||||
, sub = Sub.none
|
||||
, newSettings = Nothing
|
||||
, appEvent = AppNothing
|
||||
}
|
||||
|
||||
UiSettingsMsg lm ->
|
||||
@ -178,7 +188,7 @@ update flags settings msg model =
|
||||
{ model = { model | uiSettingsModel = res.model }
|
||||
, cmd = Cmd.map UiSettingsMsg res.cmd
|
||||
, sub = Sub.map UiSettingsMsg res.sub
|
||||
, newSettings = res.newSettings
|
||||
, appEvent = res.appEvent
|
||||
}
|
||||
|
||||
OtpSetupMsg lm ->
|
||||
@ -189,7 +199,7 @@ update flags settings msg model =
|
||||
{ model = { model | otpSetupModel = otpm }
|
||||
, cmd = Cmd.map OtpSetupMsg otpc
|
||||
, sub = Sub.none
|
||||
, newSettings = Nothing
|
||||
, appEvent = AppNothing
|
||||
}
|
||||
|
||||
NotificationHookMsg lm ->
|
||||
@ -200,7 +210,7 @@ update flags settings msg model =
|
||||
{ model = { model | notificationHookModel = hm }
|
||||
, cmd = Cmd.map NotificationHookMsg hc
|
||||
, sub = Sub.none
|
||||
, newSettings = Nothing
|
||||
, appEvent = AppNothing
|
||||
}
|
||||
|
||||
ChannelMsg lm ->
|
||||
@ -211,22 +221,9 @@ update flags settings msg model =
|
||||
{ model = { model | channelModel = cm }
|
||||
, cmd = Cmd.map ChannelMsg cc
|
||||
, sub = Sub.none
|
||||
, newSettings = Nothing
|
||||
, appEvent = AppNothing
|
||||
}
|
||||
|
||||
UpdateSettings ->
|
||||
update flags
|
||||
settings
|
||||
(UiSettingsMsg Comp.UiSettingsManage.UpdateSettings)
|
||||
model
|
||||
|
||||
ReceiveBrowserSettings sett ->
|
||||
let
|
||||
lm =
|
||||
Comp.UiSettingsManage.ReceiveBrowserSettings sett
|
||||
in
|
||||
update flags settings (UiSettingsMsg lm) model
|
||||
|
||||
PeriodicQueryMsg lm ->
|
||||
let
|
||||
( pqm, pqc, pqs ) =
|
||||
@ -235,5 +232,5 @@ update flags settings msg model =
|
||||
{ model = { model | periodicQueryModel = pqm }
|
||||
, cmd = Cmd.map PeriodicQueryMsg pqc
|
||||
, sub = Sub.map PeriodicQueryMsg pqs
|
||||
, newSettings = Nothing
|
||||
, appEvent = AppNothing
|
||||
}
|
||||
|
@ -11,9 +11,7 @@ port module Ports exposing
|
||||
, printElement
|
||||
, receiveCheckQueryResult
|
||||
, receiveServerEvent
|
||||
, receiveUiSettings
|
||||
, removeAccount
|
||||
, requestUiSettings
|
||||
, setAccount
|
||||
, setUiTheme
|
||||
)
|
||||
@ -21,7 +19,6 @@ port module Ports exposing
|
||||
import Api.Model.AuthResult exposing (AuthResult)
|
||||
import Data.QueryParseResult exposing (QueryParseResult)
|
||||
import Data.ServerEvent exposing (ServerEvent)
|
||||
import Data.UiSettings exposing (StoredUiSettings)
|
||||
import Data.UiTheme exposing (UiTheme)
|
||||
import Json.Decode as D
|
||||
|
||||
@ -46,12 +43,6 @@ port receiveCheckQueryResult : (QueryParseResult -> msg) -> Sub msg
|
||||
port initClipboard : ( String, String ) -> Cmd msg
|
||||
|
||||
|
||||
port receiveUiSettings : (StoredUiSettings -> msg) -> Sub msg
|
||||
|
||||
|
||||
port requestUiSettings : AuthResult -> Cmd msg
|
||||
|
||||
|
||||
{-| Creates a new window/tab, writes the contents of the given element
|
||||
and calls the print dialog.
|
||||
-}
|
||||
|
@ -55,25 +55,6 @@ elmApp.ports.removeAccount.subscribe(function() {
|
||||
closeWS();
|
||||
});
|
||||
|
||||
elmApp.ports.requestUiSettings.subscribe(function(args) {
|
||||
console.log("Requesting ui settings");
|
||||
var account = args;
|
||||
var collective = account ? account.collective : null;
|
||||
var user = account ? account.user : null;
|
||||
if (collective && user) {
|
||||
var key = collective + "/" + user + "/uiSettings";
|
||||
var settings = localStorage.getItem(key);
|
||||
try {
|
||||
var data = settings ? JSON.parse(settings) : null;
|
||||
if (data) {
|
||||
console.log("Sending browser ui settings");
|
||||
elmApp.ports.receiveUiSettings.send(data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var docspell_clipboards = {};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user