From c29ce73dd070bae144944950f76b7568474423b5 Mon Sep 17 00:00:00 2001 From: eikek Date: Sat, 29 Jan 2022 10:11:25 +0100 Subject: [PATCH 1/8] 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 --- modules/webapp/src/main/elm/Api.elm | 97 ++++- modules/webapp/src/main/elm/App/Data.elm | 3 +- modules/webapp/src/main/elm/App/Update.elm | 55 ++- modules/webapp/src/main/elm/Comp/MenuBar.elm | 23 +- .../src/main/elm/Comp/UiSettingsForm.elm | 335 +++++++++++----- .../src/main/elm/Comp/UiSettingsManage.elm | 377 ++++++++++++------ .../src/main/elm/Comp/UiSettingsMigrate.elm | 6 +- .../webapp/src/main/elm/Data/AccountScope.elm | 5 + modules/webapp/src/main/elm/Data/AppEvent.elm | 13 + .../webapp/src/main/elm/Data/UiSettings.elm | 170 ++++---- modules/webapp/src/main/elm/Main.elm | 2 - .../elm/Messages/Comp/UiSettingsManage.elm | 16 + .../main/elm/Messages/Page/UserSettings.elm | 5 +- .../webapp/src/main/elm/Page/Search/Data.elm | 2 +- .../src/main/elm/Page/Search/Update.elm | 32 +- .../src/main/elm/Page/UserSettings/Data.elm | 6 +- .../src/main/elm/Page/UserSettings/Update.elm | 75 ++-- modules/webapp/src/main/elm/Ports.elm | 9 - modules/webapp/src/main/webjar/docspell.js | 19 - 19 files changed, 812 insertions(+), 438 deletions(-) create mode 100644 modules/webapp/src/main/elm/Data/AppEvent.elm diff --git a/modules/webapp/src/main/elm/Api.elm b/modules/webapp/src/main/elm/Api.elm index 5a5cff99..bb989e83 100644 --- a/modules/webapp/src/main/elm/Api.elm +++ b/modules/webapp/src/main/elm/Api.elm @@ -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 diff --git a/modules/webapp/src/main/elm/App/Data.elm b/modules/webapp/src/main/elm/App/Data.elm index 0facd89b..aef87620 100644 --- a/modules/webapp/src/main/elm/App/Data.elm +++ b/modules/webapp/src/main/elm/App/Data.elm @@ -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 diff --git a/modules/webapp/src/main/elm/App/Update.elm b/modules/webapp/src/main/elm/App/Update.elm index 268e05cf..48e2621d 100644 --- a/modules/webapp/src/main/elm/App/Update.elm +++ b/modules/webapp/src/main/elm/App/Update.elm @@ -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 ] ) diff --git a/modules/webapp/src/main/elm/Comp/MenuBar.elm b/modules/webapp/src/main/elm/Comp/MenuBar.elm index b5819ae2..825b9bcd 100644 --- a/modules/webapp/src/main/elm/Comp/MenuBar.elm +++ b/modules/webapp/src/main/elm/Comp/MenuBar.elm @@ -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) ] ) [] diff --git a/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm b/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm index ecfb6781..16eb1851 100644 --- a/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm +++ b/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm @@ -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" ] diff --git a/modules/webapp/src/main/elm/Comp/UiSettingsManage.elm b/modules/webapp/src/main/elm/Comp/UiSettingsManage.elm index c1a96ecc..9bd02222 100644 --- a/modules/webapp/src/main/elm/Comp/UiSettingsManage.elm +++ b/modules/webapp/src/main/elm/Comp/UiSettingsManage.elm @@ -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" ] [] ] diff --git a/modules/webapp/src/main/elm/Comp/UiSettingsMigrate.elm b/modules/webapp/src/main/elm/Comp/UiSettingsMigrate.elm index a5b39ad3..d1aeed8e 100644 --- a/modules/webapp/src/main/elm/Comp/UiSettingsMigrate.elm +++ b/modules/webapp/src/main/elm/Comp/UiSettingsMigrate.elm @@ -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 } diff --git a/modules/webapp/src/main/elm/Data/AccountScope.elm b/modules/webapp/src/main/elm/Data/AccountScope.elm index 1fe287ec..07772f38 100644 --- a/modules/webapp/src/main/elm/Data/AccountScope.elm +++ b/modules/webapp/src/main/elm/Data/AccountScope.elm @@ -23,6 +23,11 @@ fold user coll scope = coll +all : List AccountScope +all = + [ Collective, User ] + + isUser : AccountScope -> Bool isUser scope = fold True False scope diff --git a/modules/webapp/src/main/elm/Data/AppEvent.elm b/modules/webapp/src/main/elm/Data/AppEvent.elm new file mode 100644 index 00000000..c15e912f --- /dev/null +++ b/modules/webapp/src/main/elm/Data/AppEvent.elm @@ -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 diff --git a/modules/webapp/src/main/elm/Data/UiSettings.elm b/modules/webapp/src/main/elm/Data/UiSettings.elm index 30a6c129..43f27216 100644 --- a/modules/webapp/src/main/elm/Data/UiSettings.elm +++ b/modules/webapp/src/main/elm/Data/UiSettings.elm @@ -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 } diff --git a/modules/webapp/src/main/elm/Main.elm b/modules/webapp/src/main/elm/Main.elm index 3569ad5c..d972f35b 100644 --- a/modules/webapp/src/main/elm/Main.elm +++ b/modules/webapp/src/main/elm/Main.elm @@ -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 ] diff --git a/modules/webapp/src/main/elm/Messages/Comp/UiSettingsManage.elm b/modules/webapp/src/main/elm/Messages/Comp/UiSettingsManage.elm index 77eb7037..409b0c4b 100644 --- a/modules/webapp/src/main/elm/Messages/Comp/UiSettingsManage.elm +++ b/modules/webapp/src/main/elm/Messages/Comp/UiSettingsManage.elm @@ -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" } diff --git a/modules/webapp/src/main/elm/Messages/Page/UserSettings.elm b/modules/webapp/src/main/elm/Messages/Page/UserSettings.elm index 99879774..e030fdda 100644 --- a/modules/webapp/src/main/elm/Messages/Page/UserSettings.elm +++ b/modules/webapp/src/main/elm/Messages/Page/UserSettings.elm @@ -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.""" diff --git a/modules/webapp/src/main/elm/Page/Search/Data.elm b/modules/webapp/src/main/elm/Page/Search/Data.elm index 06d6bc05..e24b2e2a 100644 --- a/modules/webapp/src/main/elm/Page/Search/Data.elm +++ b/modules/webapp/src/main/elm/Page/Search/Data.elm @@ -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 diff --git a/modules/webapp/src/main/elm/Page/Search/Update.elm b/modules/webapp/src/main/elm/Page/Search/Update.elm index 6db4874d..fa25cdb2 100644 --- a/modules/webapp/src/main/elm/Page/Search/Update.elm +++ b/modules/webapp/src/main/elm/Page/Search/Update.elm @@ -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 } diff --git a/modules/webapp/src/main/elm/Page/UserSettings/Data.elm b/modules/webapp/src/main/elm/Page/UserSettings/Data.elm index 2c71dda6..141e86e6 100644 --- a/modules/webapp/src/main/elm/Page/UserSettings/Data.elm +++ b/modules/webapp/src/main/elm/Page/UserSettings/Data.elm @@ -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 diff --git a/modules/webapp/src/main/elm/Page/UserSettings/Update.elm b/modules/webapp/src/main/elm/Page/UserSettings/Update.elm index 656674ad..eec01a4e 100644 --- a/modules/webapp/src/main/elm/Page/UserSettings/Update.elm +++ b/modules/webapp/src/main/elm/Page/UserSettings/Update.elm @@ -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 } diff --git a/modules/webapp/src/main/elm/Ports.elm b/modules/webapp/src/main/elm/Ports.elm index 8a348b85..09ff2b44 100644 --- a/modules/webapp/src/main/elm/Ports.elm +++ b/modules/webapp/src/main/elm/Ports.elm @@ -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. -} diff --git a/modules/webapp/src/main/webjar/docspell.js b/modules/webapp/src/main/webjar/docspell.js index 4b32212e..0b81ff02 100644 --- a/modules/webapp/src/main/webjar/docspell.js +++ b/modules/webapp/src/main/webjar/docspell.js @@ -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 = {}; From 1ca64f09d1dc44475e85bac5d68ecdadcd109056 Mon Sep 17 00:00:00 2001 From: eikek Date: Sat, 29 Jan 2022 10:11:54 +0100 Subject: [PATCH 2/8] Add route to get settings for a share Returns the client settings of the creator. --- .../scala/docspell/backend/ops/OShare.scala | 5 +++ .../src/main/resources/docspell-openapi.yml | 35 ++++++++++++++- .../docspell/restserver/RestServer.scala | 3 +- .../routes/ClientSettingsRoutes.scala | 44 ++++++++++++++++--- 4 files changed, 78 insertions(+), 9 deletions(-) diff --git a/modules/backend/src/main/scala/docspell/backend/ops/OShare.scala b/modules/backend/src/main/scala/docspell/backend/ops/OShare.scala index cd0bac69..75bdc27a 100644 --- a/modules/backend/src/main/scala/docspell/backend/ops/OShare.scala +++ b/modules/backend/src/main/scala/docspell/backend/ops/OShare.scala @@ -48,6 +48,8 @@ trait OShare[F[_]] { // --- + def findActiveById(id: Ident): OptionT[F, ShareData] + /** Verifies the given id and password and returns a authorization token on success. */ def verify(key: ByteVector)(id: Ident, password: Option[Password]): F[VerifyResult] @@ -277,6 +279,9 @@ object OShare { VerifyResult.invalidToken.pure[F] } + def findActiveById(id: Ident): OptionT[F, ShareData] = + RShare.findCurrentActive(id).mapK(store.transform).map(ShareData.tupled) + def findShareQuery(id: Ident): OptionT[F, ShareQuery] = RShare .findCurrentActive(id) diff --git a/modules/restapi/src/main/resources/docspell-openapi.yml b/modules/restapi/src/main/resources/docspell-openapi.yml index c585b147..11deb901 100644 --- a/modules/restapi/src/main/resources/docspell-openapi.yml +++ b/modules/restapi/src/main/resources/docspell-openapi.yml @@ -2304,7 +2304,7 @@ paths: /share/attachment/{id}/preview: head: operationId: "share-attach-check-preview" - tags: [ Attachment ] + tags: [ Share ] summary: Get the headers to a preview image of an attachment file. description: | Checks if an image file showing a preview of the attachment is @@ -2320,7 +2320,7 @@ paths: description: NotFound get: operationId: "share-attach-get-preview" - tags: [ Attachment ] + tags: [ Share ] summary: Get a preview image of an attachment file. description: | Gets a image file showing a preview of the attachment. Usually @@ -2347,6 +2347,37 @@ paths: schema: type: string format: binary + /share/clientSettings/{clientId}: + parameters: + - $ref: "#/components/parameters/clientId" + get: + operationId: "share-clientsettings-get" + tags: [ Share ] + summary: Return the client settings of current user + description: | + Returns the settings for the share. This is the settings of + the user who created the share. It is created by merging the + client settings for the collective and the user's own client + settings into one json structure. + + Null, Array, Boolean, String and Number are treated as values, + and values from the users settings completely replace values + from the collective's settings. + + The `clientId` is an identifier to a client application. It + returns a JSON structure. The server doesn't care about the + actual data, since it is meant to be interpreted by clients. + security: + - shareTokenHeader: [] + responses: + 422: + description: BadRequest + 200: + description: Ok + content: + application/json: + schema: {} + /admin/user/resetPassword: post: diff --git a/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala b/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala index dd36286e..2d85a4f9 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala @@ -213,7 +213,8 @@ object RestServer { Router( "search" -> ShareSearchRoutes(restApp.backend, cfg, token), "attachment" -> ShareAttachmentRoutes(restApp.backend, token), - "item" -> ShareItemRoutes(restApp.backend, token) + "item" -> ShareItemRoutes(restApp.backend, token), + "clientSettings" -> ClientSettingsRoutes.share(restApp.backend, token) ) def redirectTo[F[_]: Async](path: String): HttpRoutes[F] = { diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/ClientSettingsRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/ClientSettingsRoutes.scala index aef0b386..25f8a4df 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/ClientSettingsRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/ClientSettingsRoutes.scala @@ -8,10 +8,11 @@ package docspell.restserver.routes import cats.effect._ import cats.implicits._ -import cats.kernel.Semigroup +import cats.{Monad, Semigroup} +import cats.data.OptionT import docspell.backend.BackendApp -import docspell.backend.auth.AuthToken +import docspell.backend.auth.{AuthToken, ShareToken} import docspell.common._ import docspell.restapi.model._ @@ -23,6 +24,30 @@ import org.http4s.dsl.Http4sDsl object ClientSettingsRoutes { + def share[F[_]: Async]( + backend: BackendApp[F], + token: ShareToken + ): HttpRoutes[F] = { + val logger = Logger.log4s[F](org.log4s.getLogger) + + val dsl = new Http4sDsl[F] {} + import dsl._ + + HttpRoutes.of { + case GET -> Root / Ident(clientId) => + (for { + _ <- OptionT.liftF(logger.debug(s"Get client settings for share ${token.id}")) + share <- backend.share.findActiveById(token.id) + merged <- OptionT.liftF(getMergedSettings(backend, share.user.accountId, clientId)) + res <- OptionT.liftF(merged match { + case Some(j) => Ok(j) + case None => NotFound() + }) + } yield res) + .getOrElseF(Ok(Map.empty[String, String])) + } + } + def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = { val dsl = new Http4sDsl[F] {} import dsl._ @@ -30,10 +55,7 @@ object ClientSettingsRoutes { HttpRoutes.of { case GET -> Root / Ident(clientId) => for { - collData <- backend.clientSettings.loadCollective(clientId, user.account) - userData <- backend.clientSettings.loadUser(clientId, user.account) - - mergedData = collData.map(_.settingsData) |+| userData.map(_.settingsData) + mergedData <- getMergedSettings(backend, user.account, clientId) res <- mergedData match { case Some(j) => Ok(j) @@ -97,6 +119,16 @@ object ClientSettingsRoutes { } } + + def getMergedSettings[F[_]: Monad](backend:BackendApp[F], account: AccountId, clientId: Ident) = + for { + collData <- backend.clientSettings.loadCollective(clientId, account) + userData <- backend.clientSettings.loadUser(clientId, account) + + mergedData = collData.map(_.settingsData) |+| userData.map(_.settingsData) + } yield mergedData + + implicit def jsonSemigroup: Semigroup[Json] = Semigroup.instance((a1, a2) => a1.deepMerge(a2)) } From 3710f525d138b1dc9e4b186032d92f635f9332a9 Mon Sep 17 00:00:00 2001 From: eikek Date: Sat, 29 Jan 2022 10:12:15 +0100 Subject: [PATCH 3/8] Use share ui settings --- modules/webapp/src/main/elm/Api.elm | 18 ++++++++++++++++++ .../webapp/src/main/elm/Page/Share/Data.elm | 4 ++++ .../webapp/src/main/elm/Page/Share/Sidebar.elm | 7 +++---- .../webapp/src/main/elm/Page/Share/Update.elm | 11 ++++++++++- .../webapp/src/main/elm/Page/Share/View.elm | 16 +++++++--------- .../src/main/elm/Page/ShareDetail/Data.elm | 4 ++++ .../src/main/elm/Page/ShareDetail/Update.elm | 11 ++++++++++- .../src/main/elm/Page/ShareDetail/View.elm | 5 +++-- 8 files changed, 59 insertions(+), 17 deletions(-) diff --git a/modules/webapp/src/main/elm/Api.elm b/modules/webapp/src/main/elm/Api.elm index bb989e83..9489e8aa 100644 --- a/modules/webapp/src/main/elm/Api.elm +++ b/modules/webapp/src/main/elm/Api.elm @@ -22,6 +22,7 @@ module Api exposing , changeFolderName , changePassword , checkCalEvent + , clientSettingsShare , confirmMultiple , confirmOtp , createChannel @@ -2820,6 +2821,23 @@ itemDetailShare flags token itemId receive = } +clientSettingsShare : Flags -> String -> (Result Http.Error UiSettings -> msg) -> Cmd msg +clientSettingsShare flags token receive = + let + defaults = + Data.UiSettings.defaults + + decoder = + JsonDecode.map (\s -> Data.UiSettings.merge s defaults) + Data.UiSettings.storedUiSettingsDecoder + in + Http2.shareGet + { url = flags.config.baseUrl ++ "/api/v1/share/clientSettings/webClient" + , token = token + , expect = Http.expectJson receive decoder + } + + shareSendMail : Flags -> { conn : String, mail : SimpleShareMail } diff --git a/modules/webapp/src/main/elm/Page/Share/Data.elm b/modules/webapp/src/main/elm/Page/Share/Data.elm index 35cd76c5..aa96acd1 100644 --- a/modules/webapp/src/main/elm/Page/Share/Data.elm +++ b/modules/webapp/src/main/elm/Page/Share/Data.elm @@ -18,6 +18,7 @@ import Comp.SearchMenu import Comp.SharePasswordForm import Data.Flags exposing (Flags) import Data.ItemArrange exposing (ItemArrange) +import Data.UiSettings exposing (UiSettings) import Http import Page.Search.Data exposing (Msg(..)) import Set exposing (Set) @@ -53,6 +54,7 @@ type alias Model = , initialized : Bool , contentSearch : Maybe String , searchMode : SearchBarMode + , uiSettings : UiSettings , viewMode : { menuOpen : Bool , showGroups : Bool @@ -75,6 +77,7 @@ emptyModel flags = , initialized = False , contentSearch = Nothing , searchMode = SearchBarContent + , uiSettings = Data.UiSettings.defaults , viewMode = { menuOpen = False , showGroups = True @@ -107,6 +110,7 @@ type Msg = VerifyResp (Result Http.Error ShareVerifyResult) | SearchResp (Result Http.Error ItemLightList) | StatsResp Bool (Result Http.Error SearchStats) + | UiSettingsResp (Result Http.Error UiSettings) | PasswordMsg Comp.SharePasswordForm.Msg | SearchMenuMsg Comp.SearchMenu.Msg | PowerSearchMsg Comp.PowerSearchInput.Msg diff --git a/modules/webapp/src/main/elm/Page/Share/Sidebar.elm b/modules/webapp/src/main/elm/Page/Share/Sidebar.elm index 11c31457..8fbcef90 100644 --- a/modules/webapp/src/main/elm/Page/Share/Sidebar.elm +++ b/modules/webapp/src/main/elm/Page/Share/Sidebar.elm @@ -10,7 +10,6 @@ module Page.Share.Sidebar exposing (..) import Comp.SearchMenu import Comp.Tabs import Data.Flags exposing (Flags) -import Data.UiSettings exposing (UiSettings) import Html exposing (..) import Html.Attributes exposing (..) import Messages.Page.Share exposing (Texts) @@ -18,8 +17,8 @@ import Page.Share.Data exposing (Model, Msg(..)) import Util.ItemDragDrop -view : Texts -> Flags -> UiSettings -> Model -> Html Msg -view texts flags settings model = +view : Texts -> Flags -> Model -> Html Msg +view texts flags model = let hideTrashTab tab default = case tab of @@ -41,7 +40,7 @@ view texts flags settings model = ddDummy flags searchMenuCfg - settings + model.uiSettings model.searchMenuModel ) ] diff --git a/modules/webapp/src/main/elm/Page/Share/Update.elm b/modules/webapp/src/main/elm/Page/Share/Update.elm index 238c092b..a3482580 100644 --- a/modules/webapp/src/main/elm/Page/Share/Update.elm +++ b/modules/webapp/src/main/elm/Page/Share/Update.elm @@ -43,7 +43,10 @@ update flags settings shareId msg model = , verifyResult = res , searchInProgress = True } - , makeSearchCmd flags True model + , Cmd.batch + [ makeSearchCmd flags True model + , Api.clientSettingsShare flags res.token UiSettingsResp + ] ) else if res.passwordRequired then @@ -242,6 +245,12 @@ update flags settings shareId msg model = in noSub ( { model | viewMode = next }, Cmd.none ) + UiSettingsResp (Ok s) -> + noSub ( { model | uiSettings = s }, Cmd.none ) + + UiSettingsResp (Err _) -> + noSub ( model, Cmd.none ) + noSub : ( Model, Cmd Msg ) -> UpdateResult noSub ( m, c ) = diff --git a/modules/webapp/src/main/elm/Page/Share/View.elm b/modules/webapp/src/main/elm/Page/Share/View.elm index 69bbb9eb..084b8c39 100644 --- a/modules/webapp/src/main/elm/Page/Share/View.elm +++ b/modules/webapp/src/main/elm/Page/Share/View.elm @@ -11,11 +11,9 @@ import Api.Model.VersionInfo exposing (VersionInfo) import Comp.Basic as B import Comp.SharePasswordForm import Data.Flags exposing (Flags) -import Data.Items import Data.UiSettings exposing (UiSettings) import Html exposing (..) import Html.Attributes exposing (..) -import Html.Events exposing (onInput, onSubmit) import Messages.Page.Share exposing (Texts) import Page.Share.Data exposing (..) import Page.Share.Menubar as Menubar @@ -25,19 +23,19 @@ import Styles as S viewSidebar : Texts -> Bool -> Flags -> UiSettings -> Model -> Html Msg -viewSidebar texts visible flags settings model = +viewSidebar texts visible flags _ model = div [ id "sidebar" , class S.sidebar , class S.sidebarBg , classList [ ( "hidden", not visible || model.mode /= ModeShare ) ] ] - [ Sidebar.view texts flags settings model + [ Sidebar.view texts flags model ] viewContent : Texts -> Flags -> VersionInfo -> UiSettings -> String -> Model -> Html Msg -viewContent texts flags versionInfo uiSettings shareId model = +viewContent texts flags versionInfo _ shareId model = case model.mode of ModeInitial -> div @@ -60,15 +58,15 @@ viewContent texts flags versionInfo uiSettings shareId model = passwordContent texts flags versionInfo model ModeShare -> - mainContent texts flags uiSettings shareId model + mainContent texts flags shareId model --- Helpers -mainContent : Texts -> Flags -> UiSettings -> String -> Model -> Html Msg -mainContent texts flags settings shareId model = +mainContent : Texts -> Flags -> String -> Model -> Html Msg +mainContent texts flags shareId model = div [ id "content" , class "h-full flex flex-col" @@ -82,7 +80,7 @@ mainContent texts flags settings shareId model = ] , Menubar.view texts flags model , errorMessage texts model - , Results.view texts settings flags shareId model + , Results.view texts model.uiSettings flags shareId model ] diff --git a/modules/webapp/src/main/elm/Page/ShareDetail/Data.elm b/modules/webapp/src/main/elm/Page/ShareDetail/Data.elm index 2d6e1b3a..615931d8 100644 --- a/modules/webapp/src/main/elm/Page/ShareDetail/Data.elm +++ b/modules/webapp/src/main/elm/Page/ShareDetail/Data.elm @@ -14,6 +14,7 @@ import Api.Model.ShareVerifyResult exposing (ShareVerifyResult) import Comp.SharePasswordForm import Comp.UrlCopy import Data.Flags exposing (Flags) +import Data.UiSettings exposing (UiSettings) import Http @@ -37,12 +38,14 @@ type alias Model = , pageError : PageError , attachMenuOpen : Bool , visibleAttach : Int + , uiSettings : UiSettings } type Msg = VerifyResp (Result Http.Error ShareVerifyResult) | GetItemResp (Result Http.Error ItemDetail) + | UiSettingsResp (Result Http.Error UiSettings) | PasswordMsg Comp.SharePasswordForm.Msg | SelectActiveAttachment Int | ToggleSelectAttach @@ -58,6 +61,7 @@ emptyModel vm = , pageError = PageErrorNone , attachMenuOpen = False , visibleAttach = 0 + , uiSettings = Data.UiSettings.defaults } diff --git a/modules/webapp/src/main/elm/Page/ShareDetail/Update.elm b/modules/webapp/src/main/elm/Page/ShareDetail/Update.elm index efa880d3..ffcd81bf 100644 --- a/modules/webapp/src/main/elm/Page/ShareDetail/Update.elm +++ b/modules/webapp/src/main/elm/Page/ShareDetail/Update.elm @@ -25,7 +25,10 @@ update shareId itemId flags msg model = , viewMode = ViewLoading , verifyResult = res } - , Api.itemDetailShare flags res.token itemId GetItemResp + , Cmd.batch + [ Api.itemDetailShare flags res.token itemId GetItemResp + , Api.clientSettingsShare flags res.token UiSettingsResp + ] ) else if res.passwordRequired then @@ -93,3 +96,9 @@ update shareId itemId flags msg model = Comp.UrlCopy.update lm in ( model, cmd ) + + UiSettingsResp (Ok s) -> + ( { model | uiSettings = s }, Cmd.none ) + + UiSettingsResp (Err _) -> + ( model, Cmd.none ) diff --git a/modules/webapp/src/main/elm/Page/ShareDetail/View.elm b/modules/webapp/src/main/elm/Page/ShareDetail/View.elm index 210a0c12..8105598b 100644 --- a/modules/webapp/src/main/elm/Page/ShareDetail/View.elm +++ b/modules/webapp/src/main/elm/Page/ShareDetail/View.elm @@ -16,7 +16,7 @@ import Comp.UrlCopy import Data.Fields import Data.Flags exposing (Flags) import Data.Icons as Icons -import Data.ItemTemplate as IT exposing (ItemTemplate) +import Data.ItemTemplate as IT import Data.UiSettings exposing (UiSettings) import Html exposing (..) import Html.Attributes exposing (..) @@ -33,7 +33,7 @@ import Util.String viewSidebar : Texts -> Bool -> Flags -> UiSettings -> String -> String -> Model -> Html Msg -viewSidebar texts visible flags settings shareId itemId model = +viewSidebar texts visible flags _ shareId itemId model = div [ id "sidebar" , classList [ ( "hidden", not visible || model.viewMode /= ViewNormal ) ] @@ -103,6 +103,7 @@ itemData texts flags model shareId itemId = div [ class "flex ml-2 mt-1 font-semibold hover:opacity-75" , class S.basicLabel + , class (Data.UiSettings.tagColorString2 tag model.uiSettings) ] [ i [ class "fa fa-tag mr-2" ] [] , text tag.name From e1a8b9b1213fc9927c5cfb6b86e9be70d7fc617b Mon Sep 17 00:00:00 2001 From: eikek Date: Sat, 29 Jan 2022 14:15:49 +0100 Subject: [PATCH 4/8] Use collective settings for shares --- .../backend/ops/OClientSettings.scala | 11 +++++ .../src/main/resources/docspell-openapi.yml | 8 +--- .../routes/ClientSettingsRoutes.scala | 41 ++++++------------ .../src/main/elm/Comp/UiSettingsForm.elm | 3 +- .../src/main/elm/Comp/UiSettingsManage.elm | 42 ++++++++++++------- .../main/elm/Messages/Comp/UiSettingsForm.elm | 3 ++ .../elm/Messages/Comp/UiSettingsManage.elm | 7 +++- 7 files changed, 60 insertions(+), 55 deletions(-) diff --git a/modules/backend/src/main/scala/docspell/backend/ops/OClientSettings.scala b/modules/backend/src/main/scala/docspell/backend/ops/OClientSettings.scala index 4d2d71a2..560e4db4 100644 --- a/modules/backend/src/main/scala/docspell/backend/ops/OClientSettings.scala +++ b/modules/backend/src/main/scala/docspell/backend/ops/OClientSettings.scala @@ -6,6 +6,7 @@ package docspell.backend.ops +import cats.Semigroup import cats.data.OptionT import cats.effect.{Async, Resource} import cats.implicits._ @@ -32,6 +33,7 @@ trait OClientSettings[F[_]] { account: AccountId ): F[Option[RClientSettingsCollective]] + def loadMerged(clientId: Ident, account: AccountId): F[Option[Json]] } object OClientSettings { @@ -108,5 +110,14 @@ object OClientSettings { data <- OptionT(store.transact(RClientSettingsUser.find(clientId, userId))) } yield data).value + def loadMerged(clientId: Ident, account: AccountId) = + for { + collData <- loadCollective(clientId, account) + userData <- loadUser(clientId, account) + mergedData = collData.map(_.settingsData) |+| userData.map(_.settingsData) + } yield mergedData + + implicit def jsonSemigroup: Semigroup[Json] = + Semigroup.instance((a1, a2) => a1.deepMerge(a2)) }) } diff --git a/modules/restapi/src/main/resources/docspell-openapi.yml b/modules/restapi/src/main/resources/docspell-openapi.yml index 11deb901..f6dc38ef 100644 --- a/modules/restapi/src/main/resources/docspell-openapi.yml +++ b/modules/restapi/src/main/resources/docspell-openapi.yml @@ -2356,13 +2356,7 @@ paths: summary: Return the client settings of current user description: | Returns the settings for the share. This is the settings of - the user who created the share. It is created by merging the - client settings for the collective and the user's own client - settings into one json structure. - - Null, Array, Boolean, String and Number are treated as values, - and values from the users settings completely replace values - from the collective's settings. + the collective that belongs to the share. The `clientId` is an identifier to a client application. It returns a JSON structure. The server doesn't care about the diff --git a/modules/restserver/src/main/scala/docspell/restserver/routes/ClientSettingsRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/routes/ClientSettingsRoutes.scala index 25f8a4df..96dad4b4 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/routes/ClientSettingsRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/routes/ClientSettingsRoutes.scala @@ -6,10 +6,9 @@ package docspell.restserver.routes +import cats.data.OptionT import cats.effect._ import cats.implicits._ -import cats.{Monad, Semigroup} -import cats.data.OptionT import docspell.backend.BackendApp import docspell.backend.auth.{AuthToken, ShareToken} @@ -33,17 +32,15 @@ object ClientSettingsRoutes { val dsl = new Http4sDsl[F] {} import dsl._ - HttpRoutes.of { - case GET -> Root / Ident(clientId) => - (for { - _ <- OptionT.liftF(logger.debug(s"Get client settings for share ${token.id}")) - share <- backend.share.findActiveById(token.id) - merged <- OptionT.liftF(getMergedSettings(backend, share.user.accountId, clientId)) - res <- OptionT.liftF(merged match { - case Some(j) => Ok(j) - case None => NotFound() - }) - } yield res) + HttpRoutes.of { case GET -> Root / Ident(clientId) => + (for { + _ <- OptionT.liftF(logger.debug(s"Get client settings for share ${token.id}")) + share <- backend.share.findActiveById(token.id) + sett <- OptionT( + backend.clientSettings.loadCollective(clientId, share.user.accountId) + ) + res <- OptionT.liftF(Ok(sett.settingsData)) + } yield res) .getOrElseF(Ok(Map.empty[String, String])) } } @@ -55,11 +52,10 @@ object ClientSettingsRoutes { HttpRoutes.of { case GET -> Root / Ident(clientId) => for { - mergedData <- getMergedSettings(backend, user.account, clientId) - + mergedData <- backend.clientSettings.loadMerged(clientId, user.account) res <- mergedData match { case Some(j) => Ok(j) - case None => NotFound() + case None => Ok(Map.empty[String, String]) } } yield res @@ -118,17 +114,4 @@ object ClientSettingsRoutes { } yield res } } - - - def getMergedSettings[F[_]: Monad](backend:BackendApp[F], account: AccountId, clientId: Ident) = - for { - collData <- backend.clientSettings.loadCollective(clientId, account) - userData <- backend.clientSettings.loadUser(clientId, account) - - mergedData = collData.map(_.settingsData) |+| userData.map(_.settingsData) - } yield mergedData - - - implicit def jsonSemigroup: Semigroup[Json] = - Semigroup.instance((a1, a2) => a1.deepMerge(a2)) } diff --git a/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm b/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm index 16eb1851..cdae2f20 100644 --- a/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm +++ b/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm @@ -580,7 +580,6 @@ update flags sett msg model = TagsTab -> { sett | tagCategoryColors = Nothing } - -- no reset here FieldsTab -> { sett | formFields = Nothing } @@ -658,7 +657,7 @@ settingFormTabs texts flags _ model = , onClick (ResetTab tab) ] [ i [ class "fa fa-eraser mr-1" ] [] - , text "Reset" + , text texts.resetLabel ] in [ { name = akkordionTabName GeneralTab diff --git a/modules/webapp/src/main/elm/Comp/UiSettingsManage.elm b/modules/webapp/src/main/elm/Comp/UiSettingsManage.elm index 9bd02222..2f749f77 100644 --- a/modules/webapp/src/main/elm/Comp/UiSettingsManage.elm +++ b/modules/webapp/src/main/elm/Comp/UiSettingsManage.elm @@ -28,7 +28,9 @@ import Html.Attributes exposing (..) import Http import Messages.Comp.UiSettingsManage exposing (Texts) import Page.Search.Data exposing (Msg(..)) +import Process import Styles as S +import Task type alias Model = @@ -67,6 +69,7 @@ type Msg | ReceiveServerSettings (Result Http.Error ( StoredUiSettings, StoredUiSettings )) | ToggleExpandCollapse | SwitchForm AccountScope + | ResetFormState init : Flags -> ( Model, Cmd Msg ) @@ -134,12 +137,6 @@ update flags settings msg model = | collSettings = Maybe.withDefault data.collSettings sett , collModel = m_ } - , formResult = - if sett /= Nothing then - FormInit - - else - model.formResult } Data.AccountScope.User -> @@ -155,12 +152,6 @@ update flags settings msg model = | userSettings = Maybe.withDefault data.userSettings sett , userModel = m_ } - , formResult = - if sett /= Nothing then - FormInit - - else - model.formResult } Submit -> @@ -198,9 +189,13 @@ update flags settings msg model = update flags settings (ReceiveServerSettings (Ok ( data.collSettings, data.userSettings ))) - model + { model | formResult = FormSaved } + + cmd = + Process.sleep 2000 + |> Task.perform (\_ -> ResetFormState) in - { result | appEvent = AppReloadUiSettings } + { result | appEvent = AppReloadUiSettings, cmd = Cmd.batch [ cmd, result.cmd ] } _ -> unit { model | formResult = FormUnknownError } @@ -231,7 +226,13 @@ update flags settings msg model = , collSettings = coll , collModel = cm } - , formModel = ViewUser + , formModel = + case model.formModel of + ViewLoading -> + ViewUser + + _ -> + model.formModel } cmds = @@ -262,6 +263,14 @@ update flags settings msg model = in Data.AccountScope.fold forUser forColl scope + ResetFormState -> + case model.formResult of + FormSaved -> + unit { model | formResult = FormInit } + + _ -> + unit model + isError : Model -> Bool isError model = @@ -364,6 +373,9 @@ view2 texts flags _ classes model = [ h2 [ class S.header2 ] [ text texts.collectiveHeader ] + , div [ class "py-1 opacity-80" ] + [ text texts.collectiveInfo + ] , Html.map (UiFormMsg scope) (Comp.UiSettingsForm.view2 texts.uiSettingsForm diff --git a/modules/webapp/src/main/elm/Messages/Comp/UiSettingsForm.elm b/modules/webapp/src/main/elm/Messages/Comp/UiSettingsForm.elm index a09151a3..771bab51 100644 --- a/modules/webapp/src/main/elm/Messages/Comp/UiSettingsForm.elm +++ b/modules/webapp/src/main/elm/Messages/Comp/UiSettingsForm.elm @@ -56,6 +56,7 @@ type alias Texts = , fieldLabel : Field -> String , templateHelpMessage : String , pdfMode : PdfMode -> String + , resetLabel : String } @@ -131,6 +132,7 @@ and if that is not present the person. If both are absent a dash `-` is rendered. """ , pdfMode = Messages.Data.PdfMode.gb + , resetLabel = "Reset" } @@ -208,4 +210,5 @@ oder, wenn diese leer ist, die Person. Sind beide leer wird ein `-` dargestellt. """ , pdfMode = Messages.Data.PdfMode.de + , resetLabel = "Zurücksetzen" } diff --git a/modules/webapp/src/main/elm/Messages/Comp/UiSettingsManage.elm b/modules/webapp/src/main/elm/Messages/Comp/UiSettingsManage.elm index 409b0c4b..dd7d56f2 100644 --- a/modules/webapp/src/main/elm/Messages/Comp/UiSettingsManage.elm +++ b/modules/webapp/src/main/elm/Messages/Comp/UiSettingsManage.elm @@ -30,6 +30,7 @@ type alias Texts = , userHeader : String , userInfo : String , collectiveHeader : String + , collectiveInfo : String , expandCollapse : String } @@ -45,8 +46,9 @@ gb = , 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." + , userInfo = "Your personal settings override those of the collective. On reset, settings are taken from the collective settings." , collectiveHeader = "Collective settings" + , collectiveInfo = "These settings apply to all users, unless overriden by personal ones. A reset loads the provided default values of the application." , expandCollapse = "Expand/collapse all" } @@ -62,7 +64,8 @@ de = , 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." + , userInfo = "Die persönlichen Einstellungen überschreiben die des Kollektivs. Wenn Einstellungen zurückgesetzt werden, gelten automatisch die Werte des Kollektivs." , collectiveHeader = "Kollektiv Einstellungen" + , collectiveInfo = "Diese Einstellungen sind für alle Benutzer, können aber in den persönlichen Einstellungen überschrieben werden. Durch ein Zurücksetzen erhält man die bereitgestellten Standardwerte der Anwendung." , expandCollapse = "Alle ein-/ausklappen" } From 74db2d39a7634fcdb7ce1c7cfa6991d7103b6f21 Mon Sep 17 00:00:00 2001 From: eikek Date: Sat, 29 Jan 2022 14:59:39 +0100 Subject: [PATCH 5/8] Remove unused settings --- modules/webapp/src/main/elm/App/Data.elm | 7 +-- .../src/main/elm/Comp/ItemDetail/Update.elm | 2 +- .../src/main/elm/Comp/UiSettingsForm.elm | 2 +- .../webapp/src/main/elm/Data/UiSettings.elm | 57 ------------------- .../src/main/elm/Page/Search/Update.elm | 4 +- 5 files changed, 6 insertions(+), 66 deletions(-) diff --git a/modules/webapp/src/main/elm/App/Data.elm b/modules/webapp/src/main/elm/App/Data.elm index aef87620..9a2c15dd 100644 --- a/modules/webapp/src/main/elm/App/Data.elm +++ b/modules/webapp/src/main/elm/App/Data.elm @@ -18,18 +18,15 @@ import Api.Model.BasicResult exposing (BasicResult) import Api.Model.VersionInfo exposing (VersionInfo) import Browser exposing (UrlRequest) import Browser.Navigation exposing (Key) -import Data.Dashboard exposing (Dashboard) import Data.Flags exposing (Flags) import Data.ServerEvent exposing (ServerEvent) -import Data.UiSettings exposing (StoredUiSettings, UiSettings) +import Data.UiSettings exposing (UiSettings) import Data.UiTheme exposing (UiTheme) import Http -import Messages import Messages.UiLanguage exposing (UiLanguage) import Page exposing (Page(..)) import Page.CollectiveSettings.Data import Page.Dashboard.Data -import Page.Dashboard.DefaultDashboard import Page.ItemDetail.Data import Page.Login.Data import Page.ManageData.Data @@ -107,7 +104,7 @@ init key url flags_ settings = Page.Dashboard.Data.init flags searchViewMode = - if settings.searchMenuVisible then + if settings.sideMenuVisible then Page.Search.Data.SearchView else diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm index 6af8466d..8ec84d32 100644 --- a/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm +++ b/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm @@ -1411,7 +1411,7 @@ update key flags inav settings msg model = let model_ = { model - | menuOpen = settings.editMenuVisible + | menuOpen = settings.sideMenuVisible } in resultModel model_ diff --git a/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm b/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm index cdae2f20..99b92559 100644 --- a/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm +++ b/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm @@ -31,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(..), StoredUiSettings, UiSettings) +import Data.UiSettings exposing (ItemPattern, StoredUiSettings, UiSettings) import Dict exposing (Dict) import Html exposing (..) import Html.Attributes exposing (..) diff --git a/modules/webapp/src/main/elm/Data/UiSettings.elm b/modules/webapp/src/main/elm/Data/UiSettings.elm index 43f27216..4e8c8194 100644 --- a/modules/webapp/src/main/elm/Data/UiSettings.elm +++ b/modules/webapp/src/main/elm/Data/UiSettings.elm @@ -7,7 +7,6 @@ module Data.UiSettings exposing ( ItemPattern - , Pos(..) , StoredUiSettings , UiSettings , cardPreviewSize @@ -25,8 +24,6 @@ module Data.UiSettings exposing , merge , mergeDefaults , pdfUrl - , posFromString - , posToString , storedUiSettingsDecoder , storedUiSettingsEncode , tagColor @@ -66,14 +63,11 @@ type alias StoredUiSettings = , tagCategoryColors : Maybe (List ( String, String )) , pdfMode : Maybe String , itemSearchNoteLength : Maybe Int - , itemDetailNotesPosition : Maybe String , searchMenuFolderCount : Maybe Int , searchMenuTagCount : Maybe Int , searchMenuTagCatCount : Maybe Int , formFields : Maybe (List String) , itemDetailShortcuts : Maybe Bool - , searchMenuVisible : Maybe Bool - , editMenuVisible : Maybe Bool , cardPreviewSize : Maybe String , cardTitleTemplate : Maybe String , cardSubtitleTemplate : Maybe String @@ -94,14 +88,11 @@ emptyStoredSettings = , 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 @@ -133,14 +124,11 @@ storedUiSettingsDecoder = |> 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 |> P.optional "searchMenuFolderCount" maybeInt Nothing |> P.optional "searchMenuTagCount" maybeInt Nothing |> P.optional "searchMenuTagCatCount" maybeInt Nothing |> P.optional "formFields" (Decode.maybe <| Decode.list Decode.string) Nothing |> 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 @@ -168,14 +156,11 @@ storedUiSettingsEncode value = (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 @@ -203,14 +188,11 @@ type alias UiSettings = , tagCategoryColors : Dict String Color , pdfMode : PdfMode , itemSearchNoteLength : Int - , itemDetailNotesPosition : Pos , searchMenuFolderCount : Int , searchMenuTagCount : Int , searchMenuTagCatCount : Int , formFields : List Field , itemDetailShortcuts : Bool - , searchMenuVisible : Bool - , editMenuVisible : Bool , cardPreviewSize : BasicSize , cardTitleTemplate : ItemPattern , cardSubtitleTemplate : ItemPattern @@ -237,48 +219,17 @@ readPattern str = |> Maybe.map (ItemPattern str) -type Pos - = Top - | Bottom - - -posToString : Pos -> String -posToString pos = - case pos of - Top -> - "top" - - Bottom -> - "bottom" - - -posFromString : String -> Maybe Pos -posFromString str = - case str of - "top" -> - Just Top - - "bottom" -> - Just Bottom - - _ -> - Nothing - - defaults : UiSettings defaults = { itemSearchPageSize = 60 , tagCategoryColors = Dict.empty , pdfMode = Data.Pdf.Detect , itemSearchNoteLength = 0 - , itemDetailNotesPosition = Bottom , searchMenuFolderCount = 3 , searchMenuTagCount = 6 , searchMenuTagCatCount = 3 , formFields = Data.Fields.all , itemDetailShortcuts = False - , searchMenuVisible = False - , editMenuVisible = False , cardPreviewSize = Data.BasicSize.Medium , cardTitleTemplate = { template = Data.ItemTemplate.name @@ -318,9 +269,6 @@ merge given fallback = |> Maybe.withDefault fallback.pdfMode , itemSearchNoteLength = choose given.itemSearchNoteLength fallback.itemSearchNoteLength - , itemDetailNotesPosition = - choose (Maybe.andThen posFromString given.itemDetailNotesPosition) - fallback.itemDetailNotesPosition , searchMenuFolderCount = choose given.searchMenuFolderCount fallback.searchMenuFolderCount @@ -333,8 +281,6 @@ merge given fallback = (Maybe.map Data.Fields.fromList given.formFields) fallback.formFields , 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 @@ -376,7 +322,6 @@ convert settings = |> Just , pdfMode = Just (Data.Pdf.asString settings.pdfMode) , itemSearchNoteLength = Just settings.itemSearchNoteLength - , itemDetailNotesPosition = Just (posToString settings.itemDetailNotesPosition) , searchMenuFolderCount = Just settings.searchMenuFolderCount , searchMenuTagCount = Just settings.searchMenuTagCount , searchMenuTagCatCount = Just settings.searchMenuTagCatCount @@ -384,8 +329,6 @@ convert settings = List.map Data.Fields.toString settings.formFields |> Just , itemDetailShortcuts = Just settings.itemDetailShortcuts - , searchMenuVisible = Just settings.searchMenuVisible - , editMenuVisible = Just settings.editMenuVisible , cardPreviewSize = settings.cardPreviewSize |> Data.BasicSize.asString diff --git a/modules/webapp/src/main/elm/Page/Search/Update.elm b/modules/webapp/src/main/elm/Page/Search/Update.elm index fa25cdb2..b614ddad 100644 --- a/modules/webapp/src/main/elm/Page/Search/Update.elm +++ b/modules/webapp/src/main/elm/Page/Search/Update.elm @@ -644,7 +644,7 @@ update bookmarkId mId key flags texts settings msg model = SelectView { svm | mergeModel = result.model } Comp.ItemMerge.OutcomeMerged -> - if settings.searchMenuVisible then + if settings.sideMenuVisible then SearchView else @@ -836,7 +836,7 @@ update bookmarkId mId key flags texts settings msg model = UiSettingsUpdated -> let defaultViewMode = - if settings.searchMenuVisible then + if settings.sideMenuVisible then SearchView else From e4527400ca7bdbae4f7fc095963bae13315aa175 Mon Sep 17 00:00:00 2001 From: eikek Date: Sat, 29 Jan 2022 15:04:04 +0100 Subject: [PATCH 6/8] Remove more unused client code --- modules/webapp/src/main/elm/App/Data.elm | 6 +-- .../webapp/src/main/elm/Page/Search/Data.elm | 13 +------ .../src/main/elm/Page/Search/Update.elm | 39 +------------------ .../webapp/src/main/elm/Page/Search/View2.elm | 10 +---- 4 files changed, 6 insertions(+), 62 deletions(-) diff --git a/modules/webapp/src/main/elm/App/Data.elm b/modules/webapp/src/main/elm/App/Data.elm index 9a2c15dd..ca2b362c 100644 --- a/modules/webapp/src/main/elm/App/Data.elm +++ b/modules/webapp/src/main/elm/App/Data.elm @@ -104,11 +104,7 @@ init key url flags_ settings = Page.Dashboard.Data.init flags searchViewMode = - if settings.sideMenuVisible then - Page.Search.Data.SearchView - - else - Page.Search.Data.SimpleView + Page.Search.Data.SearchView in ( { flags = flags , key = key diff --git a/modules/webapp/src/main/elm/Page/Search/Data.elm b/modules/webapp/src/main/elm/Page/Search/Data.elm index e24b2e2a..694eff8f 100644 --- a/modules/webapp/src/main/elm/Page/Search/Data.elm +++ b/modules/webapp/src/main/elm/Page/Search/Data.elm @@ -109,8 +109,7 @@ initSelectViewModel flags = type ViewMode - = SimpleView - | SearchView + = SearchView | SelectView SelectViewModel | PublishView Comp.PublishItems.Model @@ -149,9 +148,6 @@ init flags viewMode = menuCollapsed : Model -> Bool menuCollapsed model = case model.viewMode of - SimpleView -> - True - SearchView -> False @@ -165,9 +161,6 @@ menuCollapsed model = selectActive : Model -> Bool selectActive model = case model.viewMode of - SimpleView -> - False - SearchView -> False @@ -181,9 +174,6 @@ selectActive model = editActive : Model -> Bool editActive model = case model.viewMode of - SimpleView -> - False - SearchView -> False @@ -203,7 +193,6 @@ type Msg | ItemSearchResp Bool (Result Http.Error ItemLightList) | ItemSearchAddResp (Result Http.Error ItemLightList) | DoSearch SearchType - | ToggleSearchMenu | ToggleSelectView | LoadMore | SetBasicSearch String diff --git a/modules/webapp/src/main/elm/Page/Search/Update.elm b/modules/webapp/src/main/elm/Page/Search/Update.elm index b614ddad..83dde95c 100644 --- a/modules/webapp/src/main/elm/Page/Search/Update.elm +++ b/modules/webapp/src/main/elm/Page/Search/Update.elm @@ -276,34 +276,10 @@ update bookmarkId mId key flags texts settings msg model = else doSearch param model - ToggleSearchMenu -> - let - nextView = - case model.viewMode of - SimpleView -> - SearchView - - SearchView -> - SimpleView - - SelectView _ -> - SimpleView - - PublishView q -> - PublishView q - in - withSub - ( { model | viewMode = nextView } - , Cmd.none - ) - ToggleSelectView -> let ( nextView, cmd ) = case model.viewMode of - SimpleView -> - ( SelectView <| initSelectViewModel flags, loadEditModel flags ) - SearchView -> ( SelectView <| initSelectViewModel flags, loadEditModel flags ) @@ -644,11 +620,7 @@ update bookmarkId mId key flags texts settings msg model = SelectView { svm | mergeModel = result.model } Comp.ItemMerge.OutcomeMerged -> - if settings.sideMenuVisible then - SearchView - - else - SimpleView + SearchView model_ = { model | viewMode = nextView } @@ -836,17 +808,10 @@ update bookmarkId mId key flags texts settings msg model = UiSettingsUpdated -> let defaultViewMode = - if settings.sideMenuVisible then - SearchView - - else - SimpleView + SearchView viewMode = case model.viewMode of - SimpleView -> - defaultViewMode - SearchView -> defaultViewMode diff --git a/modules/webapp/src/main/elm/Page/Search/View2.elm b/modules/webapp/src/main/elm/Page/Search/View2.elm index 8977bae4..534c2707 100644 --- a/modules/webapp/src/main/elm/Page/Search/View2.elm +++ b/modules/webapp/src/main/elm/Page/Search/View2.elm @@ -93,9 +93,6 @@ mainView texts flags settings model = (publishResults texts settings flags model pm) ] - SimpleView -> - Nothing - SearchView -> Nothing in @@ -109,7 +106,7 @@ mainView texts flags settings model = bookmarkQueryWidget : Texts -> UiSettings -> Flags -> Model -> List (Html Msg) -bookmarkQueryWidget texts settings flags model = +bookmarkQueryWidget texts _ _ model = case model.topWidgetModel of BookmarkQuery m -> [ div [ class "px-2 mb-4 border-l border-r border-b dark:border-slate-600" ] @@ -136,7 +133,7 @@ itemMergeView texts settings svm = publishResults : Texts -> UiSettings -> Flags -> Model -> Comp.PublishItems.Model -> List (Html Msg) -publishResults texts settings flags model pm = +publishResults texts settings flags _ pm = [ Html.map PublishViewMsg (Comp.PublishItems.view texts.publishItems settings flags pm) ] @@ -188,9 +185,6 @@ confirmModal texts model = itemsBar : Texts -> Flags -> UiSettings -> Model -> List (Html Msg) itemsBar texts flags settings model = case model.viewMode of - SimpleView -> - [ defaultMenuBar texts flags settings model ] - SearchView -> [ defaultMenuBar texts flags settings model ] From 040100f9926e4f9bcd607296bc4e7d28dde209d2 Mon Sep 17 00:00:00 2001 From: eikek Date: Sat, 29 Jan 2022 16:55:28 +0100 Subject: [PATCH 7/8] Keep form state after save --- .../webapp/src/main/elm/Comp/UiSettingsForm.elm | 10 ++++++++++ .../webapp/src/main/elm/Comp/UiSettingsManage.elm | 14 ++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm b/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm index 99b92559..87ef2f75 100644 --- a/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm +++ b/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm @@ -9,6 +9,7 @@ module Comp.UiSettingsForm exposing ( Model , Msg , init + , initData , toggleAllTabs , update , view2 @@ -178,6 +179,15 @@ init flags storedSettings defaults = ( initModel flags storedSettings defaults, Api.getTags flags "" Data.TagOrder.NameAsc GetTagsResp ) +initData : Flags -> StoredUiSettings -> UiSettings -> Model -> ( Model, Cmd Msg ) +initData flags storedSettings defaults model = + let + ( m, c ) = + init flags storedSettings defaults + in + ( { m | openTabs = model.openTabs }, c ) + + type Msg = SearchPageSizeMsg Comp.IntField.Msg | TagColorMsg Comp.ColorTagger.Msg diff --git a/modules/webapp/src/main/elm/Comp/UiSettingsManage.elm b/modules/webapp/src/main/elm/Comp/UiSettingsManage.elm index 2f749f77..26757edb 100644 --- a/modules/webapp/src/main/elm/Comp/UiSettingsManage.elm +++ b/modules/webapp/src/main/elm/Comp/UiSettingsManage.elm @@ -211,11 +211,17 @@ update flags settings msg model = userDefaults = Data.UiSettings.merge coll collDefaults - ( um, uc ) = - Comp.UiSettingsForm.init flags user userDefaults + ( ( um, uc ), ( cm, cc ) ) = + case model.formData of + Just data -> + ( Comp.UiSettingsForm.initData flags user userDefaults data.userModel + , Comp.UiSettingsForm.initData flags coll collDefaults data.collModel + ) - ( cm, cc ) = - Comp.UiSettingsForm.init flags coll collDefaults + Nothing -> + ( Comp.UiSettingsForm.init flags user userDefaults + , Comp.UiSettingsForm.init flags coll collDefaults + ) model_ = { model From e8afdf71f437093d72638663c46f0e66cd608496 Mon Sep 17 00:00:00 2001 From: eikek Date: Sat, 29 Jan 2022 17:33:13 +0100 Subject: [PATCH 8/8] Table ui tweak --- modules/webapp/src/main/elm/Comp/BoxQueryView.elm | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/webapp/src/main/elm/Comp/BoxQueryView.elm b/modules/webapp/src/main/elm/Comp/BoxQueryView.elm index c84bb9fa..574a8792 100644 --- a/modules/webapp/src/main/elm/Comp/BoxQueryView.elm +++ b/modules/webapp/src/main/elm/Comp/BoxQueryView.elm @@ -144,7 +144,12 @@ viewItemRow texts settings meta item = getColumns meta render col = - Comp.ItemColumnView.renderDiv texts.templateCtx settings col [ class "flex flex-row flex-wrap space-x-1" ] item + Comp.ItemColumnView.renderDiv + texts.templateCtx + settings + col + [ class "flex flex-row flex-wrap space-x-1 space-y-1" ] + item td1 = td [ class "py-2 px-1" ] @@ -158,7 +163,7 @@ viewItemRow texts settings meta item = tdRem index col = td - [ class "py-2 px-1" + [ class "py-1 px-1" , classList [ ( "hidden md:table-cell", index > 1 ) ] ] [ render col