Merge pull request #1327 from eikek/collective-settings

Collective settings
This commit is contained in:
mergify[bot] 2022-01-29 16:50:55 +00:00 committed by GitHub
commit 4902fb36df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 974 additions and 579 deletions

View File

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

View File

@ -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)

View File

@ -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,31 @@ 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 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
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:

View File

@ -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] = {

View File

@ -6,12 +6,12 @@
package docspell.restserver.routes
import cats.data.OptionT
import cats.effect._
import cats.implicits._
import cats.kernel.Semigroup
import docspell.backend.BackendApp
import docspell.backend.auth.AuthToken
import docspell.backend.auth.{AuthToken, ShareToken}
import docspell.common._
import docspell.restapi.model._
@ -23,6 +23,28 @@ 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)
sett <- OptionT(
backend.clientSettings.loadCollective(clientId, share.user.accountId)
)
res <- OptionT.liftF(Ok(sett.settingsData))
} 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,14 +52,10 @@ 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 <- 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
@ -96,7 +114,4 @@ object ClientSettingsRoutes {
} yield res
}
}
implicit def jsonSemigroup: Semigroup[Json] =
Semigroup.instance((a1, a2) => a1.deepMerge(a2))
}

View File

@ -22,6 +22,7 @@ module Api exposing
, changeFolderName
, changePassword
, checkCalEvent
, clientSettingsShare
, confirmMultiple
, confirmOtp
, createChannel
@ -64,6 +65,7 @@ module Api exposing
, getChannels
, getChannelsIgnoreError
, getClientSettings
, getClientSettingsRaw
, getCollective
, getCollectiveSettings
, getContacts
@ -136,6 +138,7 @@ module Api exposing
, restoreItem
, sampleEvent
, saveClientSettings
, saveUserClientSettingsBy
, searchShare
, searchShareStats
, sendMail
@ -296,7 +299,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 +2338,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 +2356,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
@ -2749,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 }

View File

@ -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,11 +104,7 @@ init key url flags_ settings =
Page.Dashboard.Data.init flags
searchViewMode =
if settings.searchMenuVisible then
Page.Search.Data.SearchView
else
Page.Search.Data.SimpleView
Page.Search.Data.SearchView
in
( { flags = flags
, key = key
@ -204,8 +197,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

View File

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

View File

@ -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

View File

@ -1411,7 +1411,7 @@ update key flags inav settings msg model =
let
model_ =
{ model
| menuOpen = settings.editMenuVisible
| menuOpen = settings.sideMenuVisible
}
in
resultModel model_

View File

@ -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)
]
)
[]

View File

@ -9,6 +9,8 @@ module Comp.UiSettingsForm exposing
( Model
, Msg
, init
, initData
, toggleAllTabs
, update
, view2
)
@ -30,7 +32,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, StoredUiSettings, UiSettings)
import Dict exposing (Dict)
import Html exposing (..)
import Html.Attributes exposing (..)
@ -73,6 +75,7 @@ type alias Model =
, uiLangModel : Comp.FixedDropdown.Model UiLanguage
, uiLang : UiLanguage
, openTabs : Set String
, defaults : UiSettings
}
@ -111,61 +114,78 @@ 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 )
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
@ -188,14 +208,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 +270,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 +286,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 +302,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 +318,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 +334,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 +349,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 +388,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 +408,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 +419,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 +443,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 +460,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 +477,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 +511,7 @@ update sett msg model =
not model.sideMenuVisible
in
( { model | sideMenuVisible = next }
, Just { sett | sideMenuVisible = next }
, Just { sett | sideMenuVisible = Just next }
)
TogglePowerSearch ->
@ -438,7 +520,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 +536,7 @@ update sett msg model =
Nothing
else
Just { sett | uiLang = newLang }
Just { sett | uiLang = Just (Messages.toIso2 newLang) }
)
PdfModeMsg lm ->
@ -470,9 +552,52 @@ 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 }
FieldsTab ->
{ sett | formFields = Nothing }
nm =
initModel flags newSettings model.defaults
in
( { nm | openTabs = model.openTabs }, Just newSettings )
--- View2
@ -495,7 +620,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 +642,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 +658,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 texts.resetLabel
]
in
[ { name = "general"
[ { name = akkordionTabName GeneralTab
, title = texts.general
, titleRight = []
, titleRight = [ resetLink GeneralTab ]
, info = Nothing
, body =
[ div [ class "mb-4 " ]
@ -560,9 +696,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 +730,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 +802,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 +836,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 +862,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 +875,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" ]

View File

@ -16,26 +16,44 @@ 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 Process
import Styles as S
import Task
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 +63,40 @@ 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
| ResetFormState
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 +105,177 @@ 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_
}
}
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_
}
}
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 | formResult = FormSaved }
cmd =
Process.sleep 2000
|> Task.perform (\_ -> ResetFormState)
in
{ result | appEvent = AppReloadUiSettings, cmd = Cmd.batch [ cmd, result.cmd ] }
_ ->
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 ), ( cm, cc ) ) =
case model.formData of
Just data ->
( Comp.UiSettingsForm.initData flags user userDefaults data.userModel
, Comp.UiSettingsForm.initData flags coll collDefaults data.collModel
)
Nothing ->
( Comp.UiSettingsForm.init flags user userDefaults
, Comp.UiSettingsForm.init flags coll collDefaults
)
model_ =
{ model
| formData =
Just
{ userSettings = user
, userModel = um
, collSettings = coll
, collModel = cm
}
, formModel =
case model.formModel of
ViewLoading ->
ViewUser
_ ->
model.formModel
}
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
}
Data.AccountScope.fold forUser forColl scope
ResetFormState ->
case model.formResult of
FormSaved ->
unit { model | formResult = FormInit }
--- View2
_ ->
unit model
isError : Model -> Bool
@ -211,7 +303,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 +317,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 +363,55 @@ 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
]
, div [ class "py-1 opacity-80" ]
[ text texts.collectiveInfo
]
, 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" ] []
]

View File

@ -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 }

View File

@ -23,6 +23,11 @@ fold user coll scope =
coll
all : List AccountScope
all =
[ Collective, User ]
isUser : AccountScope -> Bool
isUser scope =
fold True False scope

View File

@ -0,0 +1,13 @@
{-
Copyright 2020 Eike K. & Contributors
SPDX-License-Identifier: AGPL-3.0-or-later
-}
module Data.AppEvent exposing (AppEvent(..))
type AppEvent
= AppNothing
| AppReloadUiSettings

View File

@ -7,7 +7,6 @@
module Data.UiSettings exposing
( ItemPattern
, Pos(..)
, StoredUiSettings
, UiSettings
, cardPreviewSize
@ -15,22 +14,21 @@ module Data.UiSettings exposing
, catColor
, catColorFg2
, catColorString2
, convert
, defaults
, documentationSite
, emptyStoredSettings
, fieldHidden
, fieldVisible
, getUiLanguage
, merge
, mergeDefaults
, pdfUrl
, posFromString
, posToString
, storedUiSettingsDecoder
, storedUiSettingsEncode
, tagColor
, tagColorFg2
, tagColorString2
, toStoredUiSettings
)
import Api.Model.Tag exposing (Tag)
@ -62,31 +60,53 @@ 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
, searchMenuFolderCount : Maybe Int
, searchMenuTagCount : Maybe Int
, searchMenuTagCatCount : Maybe Int
, formFields : Maybe (List String)
, itemDetailShortcuts : Bool
, searchMenuVisible : Bool
, editMenuVisible : Bool
, itemDetailShortcuts : 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
, searchMenuFolderCount = Nothing
, searchMenuTagCount = Nothing
, searchMenuTagCatCount = Nothing
, formFields = Nothing
, itemDetailShortcuts = 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,66 +116,63 @@ 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
|> 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" 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 "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 "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 "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
@ -171,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
@ -205,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
@ -273,7 +256,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)
@ -285,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
@ -299,9 +280,7 @@ 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
, cardPreviewSize =
given.cardPreviewSize
|> Maybe.andThen Data.BasicSize.fromString
@ -312,17 +291,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,37 +313,35 @@ 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)
, searchMenuFolderCount = Just settings.searchMenuFolderCount
, searchMenuTagCount = Just settings.searchMenuTagCount
, searchMenuTagCatCount = Just settings.searchMenuTagCatCount
, formFields =
List.map Data.Fields.toString settings.formFields
|> Just
, itemDetailShortcuts = settings.itemDetailShortcuts
, searchMenuVisible = settings.searchMenuVisible
, editMenuVisible = settings.editMenuVisible
, itemDetailShortcuts = Just settings.itemDetailShortcuts
, 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
}

View File

@ -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
]

View File

@ -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"
}

View File

@ -15,16 +15,23 @@ 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
, collectiveInfo : String
, expandCollapse : String
}
@ -32,11 +39,17 @@ 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 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"
}
@ -44,9 +57,15 @@ 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, 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"
}

View File

@ -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."""

View File

@ -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
@ -231,7 +220,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

View File

@ -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
@ -274,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 )
@ -642,11 +620,7 @@ update bookmarkId mId key flags texts settings msg model =
SelectView { svm | mergeModel = result.model }
Comp.ItemMerge.OutcomeMerged ->
if settings.searchMenuVisible then
SearchView
else
SimpleView
SearchView
model_ =
{ model | viewMode = nextView }
@ -834,17 +808,10 @@ update bookmarkId mId key flags texts settings msg model =
UiSettingsUpdated ->
let
defaultViewMode =
if settings.searchMenuVisible then
SearchView
else
SimpleView
SearchView
viewMode =
case model.viewMode of
SimpleView ->
defaultViewMode
SearchView ->
defaultViewMode
@ -868,26 +835,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 +982,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 +1168,5 @@ makeResult ( m, c, s ) =
{ model = m
, cmd = c
, sub = s
, newSettings = Nothing
, appEvent = AppNothing
}

View File

@ -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 ]

View File

@ -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

View File

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

View File

@ -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 ) =

View File

@ -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
]

View File

@ -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
}

View File

@ -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 )

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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.
-}

View File

@ -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 = {};