mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-22 10:28:27 +00:00
Merge pull request #1327 from eikek/collective-settings
Collective settings
This commit is contained in:
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
package docspell.backend.ops
|
package docspell.backend.ops
|
||||||
|
|
||||||
|
import cats.Semigroup
|
||||||
import cats.data.OptionT
|
import cats.data.OptionT
|
||||||
import cats.effect.{Async, Resource}
|
import cats.effect.{Async, Resource}
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
@ -32,6 +33,7 @@ trait OClientSettings[F[_]] {
|
|||||||
account: AccountId
|
account: AccountId
|
||||||
): F[Option[RClientSettingsCollective]]
|
): F[Option[RClientSettingsCollective]]
|
||||||
|
|
||||||
|
def loadMerged(clientId: Ident, account: AccountId): F[Option[Json]]
|
||||||
}
|
}
|
||||||
|
|
||||||
object OClientSettings {
|
object OClientSettings {
|
||||||
@ -108,5 +110,14 @@ object OClientSettings {
|
|||||||
data <- OptionT(store.transact(RClientSettingsUser.find(clientId, userId)))
|
data <- OptionT(store.transact(RClientSettingsUser.find(clientId, userId)))
|
||||||
} yield data).value
|
} 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))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -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. */
|
/** Verifies the given id and password and returns a authorization token on success. */
|
||||||
def verify(key: ByteVector)(id: Ident, password: Option[Password]): F[VerifyResult]
|
def verify(key: ByteVector)(id: Ident, password: Option[Password]): F[VerifyResult]
|
||||||
|
|
||||||
@ -277,6 +279,9 @@ object OShare {
|
|||||||
VerifyResult.invalidToken.pure[F]
|
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] =
|
def findShareQuery(id: Ident): OptionT[F, ShareQuery] =
|
||||||
RShare
|
RShare
|
||||||
.findCurrentActive(id)
|
.findCurrentActive(id)
|
||||||
|
@ -2304,7 +2304,7 @@ paths:
|
|||||||
/share/attachment/{id}/preview:
|
/share/attachment/{id}/preview:
|
||||||
head:
|
head:
|
||||||
operationId: "share-attach-check-preview"
|
operationId: "share-attach-check-preview"
|
||||||
tags: [ Attachment ]
|
tags: [ Share ]
|
||||||
summary: Get the headers to a preview image of an attachment file.
|
summary: Get the headers to a preview image of an attachment file.
|
||||||
description: |
|
description: |
|
||||||
Checks if an image file showing a preview of the attachment is
|
Checks if an image file showing a preview of the attachment is
|
||||||
@ -2320,7 +2320,7 @@ paths:
|
|||||||
description: NotFound
|
description: NotFound
|
||||||
get:
|
get:
|
||||||
operationId: "share-attach-get-preview"
|
operationId: "share-attach-get-preview"
|
||||||
tags: [ Attachment ]
|
tags: [ Share ]
|
||||||
summary: Get a preview image of an attachment file.
|
summary: Get a preview image of an attachment file.
|
||||||
description: |
|
description: |
|
||||||
Gets a image file showing a preview of the attachment. Usually
|
Gets a image file showing a preview of the attachment. Usually
|
||||||
@ -2347,6 +2347,31 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
format: binary
|
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:
|
/admin/user/resetPassword:
|
||||||
post:
|
post:
|
||||||
|
@ -213,7 +213,8 @@ object RestServer {
|
|||||||
Router(
|
Router(
|
||||||
"search" -> ShareSearchRoutes(restApp.backend, cfg, token),
|
"search" -> ShareSearchRoutes(restApp.backend, cfg, token),
|
||||||
"attachment" -> ShareAttachmentRoutes(restApp.backend, 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] = {
|
def redirectTo[F[_]: Async](path: String): HttpRoutes[F] = {
|
||||||
|
@ -6,12 +6,12 @@
|
|||||||
|
|
||||||
package docspell.restserver.routes
|
package docspell.restserver.routes
|
||||||
|
|
||||||
|
import cats.data.OptionT
|
||||||
import cats.effect._
|
import cats.effect._
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
import cats.kernel.Semigroup
|
|
||||||
|
|
||||||
import docspell.backend.BackendApp
|
import docspell.backend.BackendApp
|
||||||
import docspell.backend.auth.AuthToken
|
import docspell.backend.auth.{AuthToken, ShareToken}
|
||||||
import docspell.common._
|
import docspell.common._
|
||||||
import docspell.restapi.model._
|
import docspell.restapi.model._
|
||||||
|
|
||||||
@ -23,6 +23,28 @@ import org.http4s.dsl.Http4sDsl
|
|||||||
|
|
||||||
object ClientSettingsRoutes {
|
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] = {
|
def apply[F[_]: Async](backend: BackendApp[F], user: AuthToken): HttpRoutes[F] = {
|
||||||
val dsl = new Http4sDsl[F] {}
|
val dsl = new Http4sDsl[F] {}
|
||||||
import dsl._
|
import dsl._
|
||||||
@ -30,14 +52,10 @@ object ClientSettingsRoutes {
|
|||||||
HttpRoutes.of {
|
HttpRoutes.of {
|
||||||
case GET -> Root / Ident(clientId) =>
|
case GET -> Root / Ident(clientId) =>
|
||||||
for {
|
for {
|
||||||
collData <- backend.clientSettings.loadCollective(clientId, user.account)
|
mergedData <- backend.clientSettings.loadMerged(clientId, user.account)
|
||||||
userData <- backend.clientSettings.loadUser(clientId, user.account)
|
|
||||||
|
|
||||||
mergedData = collData.map(_.settingsData) |+| userData.map(_.settingsData)
|
|
||||||
|
|
||||||
res <- mergedData match {
|
res <- mergedData match {
|
||||||
case Some(j) => Ok(j)
|
case Some(j) => Ok(j)
|
||||||
case None => NotFound()
|
case None => Ok(Map.empty[String, String])
|
||||||
}
|
}
|
||||||
} yield res
|
} yield res
|
||||||
|
|
||||||
@ -96,7 +114,4 @@ object ClientSettingsRoutes {
|
|||||||
} yield res
|
} yield res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit def jsonSemigroup: Semigroup[Json] =
|
|
||||||
Semigroup.instance((a1, a2) => a1.deepMerge(a2))
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ module Api exposing
|
|||||||
, changeFolderName
|
, changeFolderName
|
||||||
, changePassword
|
, changePassword
|
||||||
, checkCalEvent
|
, checkCalEvent
|
||||||
|
, clientSettingsShare
|
||||||
, confirmMultiple
|
, confirmMultiple
|
||||||
, confirmOtp
|
, confirmOtp
|
||||||
, createChannel
|
, createChannel
|
||||||
@ -64,6 +65,7 @@ module Api exposing
|
|||||||
, getChannels
|
, getChannels
|
||||||
, getChannelsIgnoreError
|
, getChannelsIgnoreError
|
||||||
, getClientSettings
|
, getClientSettings
|
||||||
|
, getClientSettingsRaw
|
||||||
, getCollective
|
, getCollective
|
||||||
, getCollectiveSettings
|
, getCollectiveSettings
|
||||||
, getContacts
|
, getContacts
|
||||||
@ -136,6 +138,7 @@ module Api exposing
|
|||||||
, restoreItem
|
, restoreItem
|
||||||
, sampleEvent
|
, sampleEvent
|
||||||
, saveClientSettings
|
, saveClientSettings
|
||||||
|
, saveUserClientSettingsBy
|
||||||
, searchShare
|
, searchShare
|
||||||
, searchShareStats
|
, searchShareStats
|
||||||
, sendMail
|
, sendMail
|
||||||
@ -296,7 +299,7 @@ import Data.OrganizationOrder exposing (OrganizationOrder)
|
|||||||
import Data.PersonOrder exposing (PersonOrder)
|
import Data.PersonOrder exposing (PersonOrder)
|
||||||
import Data.Priority exposing (Priority)
|
import Data.Priority exposing (Priority)
|
||||||
import Data.TagOrder exposing (TagOrder)
|
import Data.TagOrder exposing (TagOrder)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (StoredUiSettings, UiSettings)
|
||||||
import File exposing (File)
|
import File exposing (File)
|
||||||
import Http
|
import Http
|
||||||
import Json.Decode as JsonDecode
|
import Json.Decode as JsonDecode
|
||||||
@ -2335,6 +2338,13 @@ getItemProposals flags item receive =
|
|||||||
--- Client Settings
|
--- 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 -> (Result Http.Error UiSettings -> msg) -> Cmd msg
|
||||||
getClientSettings flags receive =
|
getClientSettings flags receive =
|
||||||
let
|
let
|
||||||
@ -2346,29 +2356,91 @@ getClientSettings flags receive =
|
|||||||
Data.UiSettings.storedUiSettingsDecoder
|
Data.UiSettings.storedUiSettingsDecoder
|
||||||
in
|
in
|
||||||
Http2.authGet
|
Http2.authGet
|
||||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/clientSettings/user/webClient"
|
{ url = flags.config.baseUrl ++ "/api/v1/sec/clientSettings/webClient"
|
||||||
, account = getAccount flags
|
, account = getAccount flags
|
||||||
, expect = Http.expectJson receive decoder
|
, expect = Http.expectJson receive decoder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
saveClientSettings : Flags -> UiSettings -> (Result Http.Error BasicResult -> msg) -> Cmd msg
|
getClientSettingsTaskFor : Flags -> AccountScope -> Task.Task Http.Error StoredUiSettings
|
||||||
saveClientSettings flags settings receive =
|
getClientSettingsTaskFor flags scope =
|
||||||
let
|
let
|
||||||
storedSettings =
|
path =
|
||||||
Data.UiSettings.toStoredUiSettings settings
|
uiSettingsPath scope
|
||||||
|
|
||||||
encode =
|
|
||||||
Data.UiSettings.storedUiSettingsEncode storedSettings
|
|
||||||
in
|
in
|
||||||
Http2.authPut
|
Http2.authTask
|
||||||
{ url = flags.config.baseUrl ++ "/api/v1/sec/clientSettings/user/webClient"
|
{ method = "GET"
|
||||||
|
, url = flags.config.baseUrl ++ path
|
||||||
, account = getAccount flags
|
, account = getAccount flags
|
||||||
, body = Http.jsonBody encode
|
, body = Http.emptyBody
|
||||||
, expect = Http.expectJson receive Api.Model.BasicResult.decoder
|
, 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
|
--- 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 :
|
shareSendMail :
|
||||||
Flags
|
Flags
|
||||||
-> { conn : String, mail : SimpleShareMail }
|
-> { conn : String, mail : SimpleShareMail }
|
||||||
|
@ -18,18 +18,15 @@ import Api.Model.BasicResult exposing (BasicResult)
|
|||||||
import Api.Model.VersionInfo exposing (VersionInfo)
|
import Api.Model.VersionInfo exposing (VersionInfo)
|
||||||
import Browser exposing (UrlRequest)
|
import Browser exposing (UrlRequest)
|
||||||
import Browser.Navigation exposing (Key)
|
import Browser.Navigation exposing (Key)
|
||||||
import Data.Dashboard exposing (Dashboard)
|
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.ServerEvent exposing (ServerEvent)
|
import Data.ServerEvent exposing (ServerEvent)
|
||||||
import Data.UiSettings exposing (StoredUiSettings, UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
import Data.UiTheme exposing (UiTheme)
|
import Data.UiTheme exposing (UiTheme)
|
||||||
import Http
|
import Http
|
||||||
import Messages
|
|
||||||
import Messages.UiLanguage exposing (UiLanguage)
|
import Messages.UiLanguage exposing (UiLanguage)
|
||||||
import Page exposing (Page(..))
|
import Page exposing (Page(..))
|
||||||
import Page.CollectiveSettings.Data
|
import Page.CollectiveSettings.Data
|
||||||
import Page.Dashboard.Data
|
import Page.Dashboard.Data
|
||||||
import Page.Dashboard.DefaultDashboard
|
|
||||||
import Page.ItemDetail.Data
|
import Page.ItemDetail.Data
|
||||||
import Page.Login.Data
|
import Page.Login.Data
|
||||||
import Page.ManageData.Data
|
import Page.ManageData.Data
|
||||||
@ -107,11 +104,7 @@ init key url flags_ settings =
|
|||||||
Page.Dashboard.Data.init flags
|
Page.Dashboard.Data.init flags
|
||||||
|
|
||||||
searchViewMode =
|
searchViewMode =
|
||||||
if settings.searchMenuVisible then
|
Page.Search.Data.SearchView
|
||||||
Page.Search.Data.SearchView
|
|
||||||
|
|
||||||
else
|
|
||||||
Page.Search.Data.SimpleView
|
|
||||||
in
|
in
|
||||||
( { flags = flags
|
( { flags = flags
|
||||||
, key = key
|
, key = key
|
||||||
@ -204,8 +197,7 @@ type Msg
|
|||||||
| ToggleDarkMode
|
| ToggleDarkMode
|
||||||
| ToggleLangMenu
|
| ToggleLangMenu
|
||||||
| SetLanguage UiLanguage
|
| SetLanguage UiLanguage
|
||||||
| ClientSettingsSaveResp UiSettings (Result Http.Error BasicResult)
|
| ClientSettingsSaveResp (Result Http.Error BasicResult)
|
||||||
| ReceiveBrowserSettings StoredUiSettings
|
|
||||||
| ReceiveWsMessage (Result String ServerEvent)
|
| ReceiveWsMessage (Result String ServerEvent)
|
||||||
| ToggleShowNewItemsArrived
|
| ToggleShowNewItemsArrived
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import Api
|
|||||||
import App.Data exposing (..)
|
import App.Data exposing (..)
|
||||||
import Browser exposing (UrlRequest(..))
|
import Browser exposing (UrlRequest(..))
|
||||||
import Browser.Navigation as Nav
|
import Browser.Navigation as Nav
|
||||||
|
import Data.AppEvent exposing (AppEvent(..))
|
||||||
import Data.Flags
|
import Data.Flags
|
||||||
import Data.ServerEvent exposing (ServerEvent(..))
|
import Data.ServerEvent exposing (ServerEvent(..))
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
@ -81,15 +82,15 @@ updateWithSub msg model =
|
|||||||
next =
|
next =
|
||||||
Data.UiTheme.cycle settings.uiTheme
|
Data.UiTheme.cycle settings.uiTheme
|
||||||
|
|
||||||
newSettings =
|
newSettings s =
|
||||||
{ settings | uiTheme = next }
|
{ s | uiTheme = Just (Data.UiTheme.toString next) }
|
||||||
in
|
in
|
||||||
-- when authenticated, store it in settings only
|
-- when authenticated, store it in settings only
|
||||||
-- once new settings are successfully saved (the
|
-- once new settings are successfully saved (the
|
||||||
-- response is arrived), the ui is updated. so it
|
-- response is arrived), the ui is updated. so it
|
||||||
-- is also updated on page refresh
|
-- is also updated on page refresh
|
||||||
( { model | userMenuOpen = False }
|
( { model | userMenuOpen = False }
|
||||||
, Api.saveClientSettings model.flags newSettings (ClientSettingsSaveResp newSettings)
|
, Api.saveUserClientSettingsBy model.flags newSettings ClientSettingsSaveResp
|
||||||
, Sub.none
|
, Sub.none
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -104,14 +105,14 @@ updateWithSub msg model =
|
|||||||
, Sub.none
|
, Sub.none
|
||||||
)
|
)
|
||||||
|
|
||||||
ClientSettingsSaveResp settings (Ok res) ->
|
ClientSettingsSaveResp (Ok res) ->
|
||||||
if res.success then
|
if res.success then
|
||||||
applyClientSettings texts model settings
|
( model, Api.getClientSettings model.flags GetUiSettings, Sub.none )
|
||||||
|
|
||||||
else
|
else
|
||||||
( model, Cmd.none, Sub.none )
|
( model, Cmd.none, Sub.none )
|
||||||
|
|
||||||
ClientSettingsSaveResp _ (Err _) ->
|
ClientSettingsSaveResp (Err _) ->
|
||||||
( model, Cmd.none, Sub.none )
|
( model, Cmd.none, Sub.none )
|
||||||
|
|
||||||
ToggleLangMenu ->
|
ToggleLangMenu ->
|
||||||
@ -307,13 +308,6 @@ updateWithSub msg model =
|
|||||||
GetUiSettings (Err _) ->
|
GetUiSettings (Err _) ->
|
||||||
( model, Cmd.none, Sub.none )
|
( model, Cmd.none, Sub.none )
|
||||||
|
|
||||||
ReceiveBrowserSettings sett ->
|
|
||||||
let
|
|
||||||
lm =
|
|
||||||
Page.UserSettings.Data.ReceiveBrowserSettings sett
|
|
||||||
in
|
|
||||||
updateUserSettings texts lm model
|
|
||||||
|
|
||||||
ReceiveWsMessage data ->
|
ReceiveWsMessage data ->
|
||||||
case data of
|
case data of
|
||||||
Ok (JobDone task) ->
|
Ok (JobDone task) ->
|
||||||
@ -342,7 +336,7 @@ updateWithSub msg model =
|
|||||||
Ok (JobsWaiting n) ->
|
Ok (JobsWaiting n) ->
|
||||||
( { model | jobsWaiting = max 0 n }, Cmd.none, Sub.none )
|
( { model | jobsWaiting = max 0 n }, Cmd.none, Sub.none )
|
||||||
|
|
||||||
Err err ->
|
Err _ ->
|
||||||
( model, Cmd.none, Sub.none )
|
( model, Cmd.none, Sub.none )
|
||||||
|
|
||||||
ToggleShowNewItemsArrived ->
|
ToggleShowNewItemsArrived ->
|
||||||
@ -368,7 +362,6 @@ applyClientSettings texts model settings =
|
|||||||
, Sub.none
|
, Sub.none
|
||||||
)
|
)
|
||||||
, updateDashboard texts Page.Dashboard.Data.reloadUiSettings
|
, updateDashboard texts Page.Dashboard.Data.reloadUiSettings
|
||||||
, updateUserSettings texts Page.UserSettings.Data.UpdateSettings
|
|
||||||
, updateSearch texts Page.Search.Data.UiSettingsUpdated
|
, updateSearch texts Page.Search.Data.UiSettingsUpdated
|
||||||
, updateItemDetail texts Page.ItemDetail.Data.UiSettingsUpdated
|
, updateItemDetail texts Page.ItemDetail.Data.UiSettingsUpdated
|
||||||
]
|
]
|
||||||
@ -524,22 +517,21 @@ updateUserSettings texts lmsg model =
|
|||||||
model_ =
|
model_ =
|
||||||
{ model | userSettingsModel = result.model }
|
{ model | userSettingsModel = result.model }
|
||||||
|
|
||||||
( lm2, lc2, s2 ) =
|
lc =
|
||||||
case result.newSettings of
|
case result.appEvent of
|
||||||
Just sett ->
|
AppReloadUiSettings ->
|
||||||
applyClientSettings texts model_ sett
|
Api.getClientSettings model.flags GetUiSettings
|
||||||
|
|
||||||
Nothing ->
|
AppNothing ->
|
||||||
( model_, Cmd.none, Sub.none )
|
Cmd.none
|
||||||
in
|
in
|
||||||
( lm2
|
( model_
|
||||||
, Cmd.batch
|
, Cmd.batch
|
||||||
[ Cmd.map UserSettingsMsg result.cmd
|
[ Cmd.map UserSettingsMsg result.cmd
|
||||||
, lc2
|
, lc
|
||||||
]
|
]
|
||||||
, Sub.batch
|
, Sub.batch
|
||||||
[ Sub.map UserSettingsMsg result.sub
|
[ Sub.map UserSettingsMsg result.sub
|
||||||
, s2
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -595,22 +587,21 @@ updateSearch texts lmsg model =
|
|||||||
model_ =
|
model_ =
|
||||||
{ model | searchModel = result.model }
|
{ model | searchModel = result.model }
|
||||||
|
|
||||||
( lm, lc, ls ) =
|
lc =
|
||||||
case result.newSettings of
|
case result.appEvent of
|
||||||
Just sett ->
|
AppReloadUiSettings ->
|
||||||
applyClientSettings texts model_ sett
|
Api.getClientSettings model.flags GetUiSettings
|
||||||
|
|
||||||
Nothing ->
|
AppNothing ->
|
||||||
( model_, Cmd.none, Sub.none )
|
Cmd.none
|
||||||
in
|
in
|
||||||
( lm
|
( model_
|
||||||
, Cmd.batch
|
, Cmd.batch
|
||||||
[ Cmd.map SearchMsg result.cmd
|
[ Cmd.map SearchMsg result.cmd
|
||||||
, lc
|
, lc
|
||||||
]
|
]
|
||||||
, Sub.batch
|
, Sub.batch
|
||||||
[ Sub.map SearchMsg result.sub
|
[ Sub.map SearchMsg result.sub
|
||||||
, ls
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -144,7 +144,12 @@ viewItemRow texts settings meta item =
|
|||||||
getColumns meta
|
getColumns meta
|
||||||
|
|
||||||
render col =
|
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 =
|
td1 =
|
||||||
td [ class "py-2 px-1" ]
|
td [ class "py-2 px-1" ]
|
||||||
@ -158,7 +163,7 @@ viewItemRow texts settings meta item =
|
|||||||
|
|
||||||
tdRem index col =
|
tdRem index col =
|
||||||
td
|
td
|
||||||
[ class "py-2 px-1"
|
[ class "py-1 px-1"
|
||||||
, classList [ ( "hidden md:table-cell", index > 1 ) ]
|
, classList [ ( "hidden md:table-cell", index > 1 ) ]
|
||||||
]
|
]
|
||||||
[ render col
|
[ render col
|
||||||
|
@ -1411,7 +1411,7 @@ update key flags inav settings msg model =
|
|||||||
let
|
let
|
||||||
model_ =
|
model_ =
|
||||||
{ model
|
{ model
|
||||||
| menuOpen = settings.editMenuVisible
|
| menuOpen = settings.sideMenuVisible
|
||||||
}
|
}
|
||||||
in
|
in
|
||||||
resultModel model_
|
resultModel model_
|
||||||
|
@ -26,6 +26,7 @@ import Styles as S
|
|||||||
type Item msg
|
type Item msg
|
||||||
= TextInput (TextInputData msg)
|
= TextInput (TextInputData msg)
|
||||||
| Checkbox (CheckboxData msg)
|
| Checkbox (CheckboxData msg)
|
||||||
|
| RadioButton (CheckboxData msg)
|
||||||
| PrimaryButton (ButtonData msg)
|
| PrimaryButton (ButtonData msg)
|
||||||
| SecondaryButton (ButtonData msg)
|
| SecondaryButton (ButtonData msg)
|
||||||
| DeleteButton (ButtonData msg)
|
| DeleteButton (ButtonData msg)
|
||||||
@ -119,7 +120,7 @@ view1 classes mb =
|
|||||||
(List.map viewItem mb.start)
|
(List.map viewItem mb.start)
|
||||||
|
|
||||||
right =
|
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)
|
(List.map viewItem mb.end)
|
||||||
in
|
in
|
||||||
div
|
div
|
||||||
@ -139,7 +140,10 @@ viewItem item =
|
|||||||
makeInput model
|
makeInput model
|
||||||
|
|
||||||
Checkbox model ->
|
Checkbox model ->
|
||||||
makeCheckbox model
|
makeCheckbox False model
|
||||||
|
|
||||||
|
RadioButton model ->
|
||||||
|
makeCheckbox True model
|
||||||
|
|
||||||
PrimaryButton model ->
|
PrimaryButton model ->
|
||||||
makeButton [ ( S.primaryButton, True ) ] model
|
makeButton [ ( S.primaryButton, True ) ] model
|
||||||
@ -306,8 +310,8 @@ makeButton btnType model =
|
|||||||
(icon ++ label)
|
(icon ++ label)
|
||||||
|
|
||||||
|
|
||||||
makeCheckbox : CheckboxData msg -> Html msg
|
makeCheckbox : Bool -> CheckboxData msg -> Html msg
|
||||||
makeCheckbox model =
|
makeCheckbox radio model =
|
||||||
let
|
let
|
||||||
withId list =
|
withId list =
|
||||||
if model.id == "" then
|
if model.id == "" then
|
||||||
@ -315,6 +319,13 @@ makeCheckbox model =
|
|||||||
|
|
||||||
else
|
else
|
||||||
id model.id :: list
|
id model.id :: list
|
||||||
|
|
||||||
|
fold rd ck =
|
||||||
|
if radio then
|
||||||
|
rd
|
||||||
|
|
||||||
|
else
|
||||||
|
ck
|
||||||
in
|
in
|
||||||
div [ class "" ]
|
div [ class "" ]
|
||||||
[ label
|
[ label
|
||||||
@ -323,10 +334,10 @@ makeCheckbox model =
|
|||||||
]
|
]
|
||||||
[ input
|
[ input
|
||||||
(withId
|
(withId
|
||||||
[ type_ "checkbox"
|
[ type_ (fold "radio" "checkbox")
|
||||||
, onCheck model.tagger
|
, onCheck model.tagger
|
||||||
, checked model.value
|
, checked model.value
|
||||||
, class S.checkboxInput
|
, class (fold S.radioInput S.checkboxInput)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
[]
|
[]
|
||||||
|
@ -9,6 +9,8 @@ module Comp.UiSettingsForm exposing
|
|||||||
( Model
|
( Model
|
||||||
, Msg
|
, Msg
|
||||||
, init
|
, init
|
||||||
|
, initData
|
||||||
|
, toggleAllTabs
|
||||||
, update
|
, update
|
||||||
, view2
|
, view2
|
||||||
)
|
)
|
||||||
@ -30,7 +32,7 @@ import Data.Flags exposing (Flags)
|
|||||||
import Data.ItemTemplate as IT exposing (ItemTemplate)
|
import Data.ItemTemplate as IT exposing (ItemTemplate)
|
||||||
import Data.Pdf exposing (PdfMode)
|
import Data.Pdf exposing (PdfMode)
|
||||||
import Data.TagOrder
|
import Data.TagOrder
|
||||||
import Data.UiSettings exposing (ItemPattern, Pos(..), UiSettings)
|
import Data.UiSettings exposing (ItemPattern, StoredUiSettings, UiSettings)
|
||||||
import Dict exposing (Dict)
|
import Dict exposing (Dict)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
@ -73,6 +75,7 @@ type alias Model =
|
|||||||
, uiLangModel : Comp.FixedDropdown.Model UiLanguage
|
, uiLangModel : Comp.FixedDropdown.Model UiLanguage
|
||||||
, uiLang : UiLanguage
|
, uiLang : UiLanguage
|
||||||
, openTabs : Set String
|
, openTabs : Set String
|
||||||
|
, defaults : UiSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -111,61 +114,78 @@ updatePatternModel pm str =
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
init : Flags -> UiSettings -> ( Model, Cmd Msg )
|
initModel : Flags -> StoredUiSettings -> UiSettings -> Model
|
||||||
init flags settings =
|
initModel flags storedSettings defaults =
|
||||||
( { itemSearchPageSize = Just settings.itemSearchPageSize
|
let
|
||||||
, searchPageSizeModel =
|
settings =
|
||||||
Comp.IntField.init
|
Data.UiSettings.merge storedSettings defaults
|
||||||
(Just 10)
|
in
|
||||||
(Just flags.config.maxPageSize)
|
{ itemSearchPageSize = Just settings.itemSearchPageSize
|
||||||
False
|
, searchPageSizeModel =
|
||||||
, tagColors = settings.tagCategoryColors
|
Comp.IntField.init
|
||||||
, tagColorModel =
|
(Just 10)
|
||||||
Comp.ColorTagger.init
|
(Just flags.config.maxPageSize)
|
||||||
[]
|
False
|
||||||
Data.Color.all
|
, tagColors = settings.tagCategoryColors
|
||||||
, pdfMode = settings.pdfMode
|
, tagColorModel =
|
||||||
, pdfModeModel = Comp.FixedDropdown.init Data.Pdf.allModes
|
Comp.ColorTagger.init
|
||||||
, itemSearchNoteLength = Just settings.itemSearchNoteLength
|
[]
|
||||||
, searchNoteLengthModel =
|
Data.Color.all
|
||||||
Comp.IntField.init
|
, pdfMode = settings.pdfMode
|
||||||
(Just 0)
|
, pdfModeModel = Comp.FixedDropdown.init Data.Pdf.allModes
|
||||||
(Just flags.config.maxNoteLength)
|
, itemSearchNoteLength = Just settings.itemSearchNoteLength
|
||||||
False
|
, searchNoteLengthModel =
|
||||||
, searchMenuFolderCount = Just settings.searchMenuFolderCount
|
Comp.IntField.init
|
||||||
, searchMenuFolderCountModel =
|
(Just 0)
|
||||||
Comp.IntField.init
|
(Just flags.config.maxNoteLength)
|
||||||
(Just 0)
|
False
|
||||||
(Just 2000)
|
, searchMenuFolderCount = Just settings.searchMenuFolderCount
|
||||||
False
|
, searchMenuFolderCountModel =
|
||||||
, searchMenuTagCount = Just settings.searchMenuTagCount
|
Comp.IntField.init
|
||||||
, searchMenuTagCountModel =
|
(Just 0)
|
||||||
Comp.IntField.init
|
(Just 2000)
|
||||||
(Just 0)
|
False
|
||||||
(Just 2000)
|
, searchMenuTagCount = Just settings.searchMenuTagCount
|
||||||
False
|
, searchMenuTagCountModel =
|
||||||
, searchMenuTagCatCount = Just settings.searchMenuTagCatCount
|
Comp.IntField.init
|
||||||
, searchMenuTagCatCountModel =
|
(Just 0)
|
||||||
Comp.IntField.init
|
(Just 2000)
|
||||||
(Just 0)
|
False
|
||||||
(Just 2000)
|
, searchMenuTagCatCount = Just settings.searchMenuTagCatCount
|
||||||
False
|
, searchMenuTagCatCountModel =
|
||||||
, formFields = settings.formFields
|
Comp.IntField.init
|
||||||
, itemDetailShortcuts = settings.itemDetailShortcuts
|
(Just 0)
|
||||||
, cardPreviewSize = settings.cardPreviewSize
|
(Just 2000)
|
||||||
, cardTitlePattern = initPatternModel settings.cardTitleTemplate
|
False
|
||||||
, cardSubtitlePattern = initPatternModel settings.cardSubtitleTemplate
|
, formFields = settings.formFields
|
||||||
, showPatternHelp = False
|
, itemDetailShortcuts = settings.itemDetailShortcuts
|
||||||
, searchStatsVisible = settings.searchStatsVisible
|
, cardPreviewSize = settings.cardPreviewSize
|
||||||
, sideMenuVisible = settings.sideMenuVisible
|
, cardTitlePattern = initPatternModel settings.cardTitleTemplate
|
||||||
, powerSearchEnabled = settings.powerSearchEnabled
|
, cardSubtitlePattern = initPatternModel settings.cardSubtitleTemplate
|
||||||
, uiLang = settings.uiLang
|
, showPatternHelp = False
|
||||||
, uiLangModel =
|
, searchStatsVisible = settings.searchStatsVisible
|
||||||
Comp.FixedDropdown.init Messages.UiLanguage.all
|
, sideMenuVisible = settings.sideMenuVisible
|
||||||
, openTabs = Set.empty
|
, powerSearchEnabled = settings.powerSearchEnabled
|
||||||
}
|
, uiLang = settings.uiLang
|
||||||
, Api.getTags flags "" Data.TagOrder.NameAsc GetTagsResp
|
, 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
|
type Msg
|
||||||
@ -188,14 +208,61 @@ type Msg
|
|||||||
| TogglePowerSearch
|
| TogglePowerSearch
|
||||||
| UiLangMsg (Comp.FixedDropdown.Msg UiLanguage)
|
| UiLangMsg (Comp.FixedDropdown.Msg UiLanguage)
|
||||||
| PdfModeMsg (Comp.FixedDropdown.Msg PdfMode)
|
| 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
|
||||||
|
|
||||||
|
|
||||||
update : UiSettings -> Msg -> Model -> ( Model, Maybe UiSettings )
|
update : Flags -> StoredUiSettings -> Msg -> Model -> ( Model, Maybe StoredUiSettings )
|
||||||
update sett msg model =
|
update flags sett msg model =
|
||||||
case msg of
|
case msg of
|
||||||
SearchPageSizeMsg lm ->
|
SearchPageSizeMsg lm ->
|
||||||
let
|
let
|
||||||
@ -203,7 +270,7 @@ update sett msg model =
|
|||||||
Comp.IntField.update lm model.searchPageSizeModel
|
Comp.IntField.update lm model.searchPageSizeModel
|
||||||
|
|
||||||
nextSettings =
|
nextSettings =
|
||||||
Maybe.map (\sz -> { sett | itemSearchPageSize = sz }) n
|
Maybe.map (\sz -> { sett | itemSearchPageSize = Just sz }) n
|
||||||
|
|
||||||
model_ =
|
model_ =
|
||||||
{ model
|
{ model
|
||||||
@ -219,7 +286,7 @@ update sett msg model =
|
|||||||
Comp.IntField.update lm model.searchNoteLengthModel
|
Comp.IntField.update lm model.searchNoteLengthModel
|
||||||
|
|
||||||
nextSettings =
|
nextSettings =
|
||||||
Maybe.map (\len -> { sett | itemSearchNoteLength = len }) n
|
Maybe.map (\len -> { sett | itemSearchNoteLength = Just len }) n
|
||||||
|
|
||||||
model_ =
|
model_ =
|
||||||
{ model
|
{ model
|
||||||
@ -235,7 +302,7 @@ update sett msg model =
|
|||||||
Comp.IntField.update lm model.searchMenuFolderCountModel
|
Comp.IntField.update lm model.searchMenuFolderCountModel
|
||||||
|
|
||||||
nextSettings =
|
nextSettings =
|
||||||
Maybe.map (\len -> { sett | searchMenuFolderCount = len }) n
|
Maybe.map (\len -> { sett | searchMenuFolderCount = Just len }) n
|
||||||
|
|
||||||
model_ =
|
model_ =
|
||||||
{ model
|
{ model
|
||||||
@ -251,7 +318,7 @@ update sett msg model =
|
|||||||
Comp.IntField.update lm model.searchMenuTagCountModel
|
Comp.IntField.update lm model.searchMenuTagCountModel
|
||||||
|
|
||||||
nextSettings =
|
nextSettings =
|
||||||
Maybe.map (\len -> { sett | searchMenuTagCount = len }) n
|
Maybe.map (\len -> { sett | searchMenuTagCount = Just len }) n
|
||||||
|
|
||||||
model_ =
|
model_ =
|
||||||
{ model
|
{ model
|
||||||
@ -267,7 +334,7 @@ update sett msg model =
|
|||||||
Comp.IntField.update lm model.searchMenuTagCatCountModel
|
Comp.IntField.update lm model.searchMenuTagCatCountModel
|
||||||
|
|
||||||
nextSettings =
|
nextSettings =
|
||||||
Maybe.map (\len -> { sett | searchMenuTagCatCount = len }) n
|
Maybe.map (\len -> { sett | searchMenuTagCatCount = Just len }) n
|
||||||
|
|
||||||
model_ =
|
model_ =
|
||||||
{ model
|
{ model
|
||||||
@ -282,8 +349,13 @@ update sett msg model =
|
|||||||
( m_, d_ ) =
|
( m_, d_ ) =
|
||||||
Comp.ColorTagger.update lm model.tagColorModel
|
Comp.ColorTagger.update lm model.tagColorModel
|
||||||
|
|
||||||
|
colors dict =
|
||||||
|
Dict.map (\_ -> Data.Color.toString) dict
|
||||||
|
|> Dict.toList
|
||||||
|
|> Just
|
||||||
|
|
||||||
nextSettings =
|
nextSettings =
|
||||||
Maybe.map (\tc -> { sett | tagCategoryColors = tc }) d_
|
Maybe.map (\tc -> { sett | tagCategoryColors = colors tc }) d_
|
||||||
|
|
||||||
model_ =
|
model_ =
|
||||||
{ model
|
{ model
|
||||||
@ -316,7 +388,11 @@ update sett msg model =
|
|||||||
Comp.FieldListSelect.update lm model.formFields
|
Comp.FieldListSelect.update lm model.formFields
|
||||||
|
|
||||||
newSettings =
|
newSettings =
|
||||||
{ sett | formFields = selected }
|
{ sett
|
||||||
|
| formFields =
|
||||||
|
List.map Data.Fields.toString selected
|
||||||
|
|> Just
|
||||||
|
}
|
||||||
in
|
in
|
||||||
( { model | formFields = selected }
|
( { model | formFields = selected }
|
||||||
, if selected /= model.formFields then
|
, if selected /= model.formFields then
|
||||||
@ -332,7 +408,7 @@ update sett msg model =
|
|||||||
not model.itemDetailShortcuts
|
not model.itemDetailShortcuts
|
||||||
in
|
in
|
||||||
( { model | itemDetailShortcuts = flag }
|
( { model | itemDetailShortcuts = flag }
|
||||||
, Just { sett | itemDetailShortcuts = flag }
|
, Just { sett | itemDetailShortcuts = Just flag }
|
||||||
)
|
)
|
||||||
|
|
||||||
CardPreviewSizeMsg lm ->
|
CardPreviewSizeMsg lm ->
|
||||||
@ -343,7 +419,13 @@ update sett msg model =
|
|||||||
|
|
||||||
newSettings =
|
newSettings =
|
||||||
if next /= model.cardPreviewSize then
|
if next /= model.cardPreviewSize then
|
||||||
Just { sett | cardPreviewSize = next }
|
Just
|
||||||
|
{ sett
|
||||||
|
| cardPreviewSize =
|
||||||
|
next
|
||||||
|
|> Data.BasicSize.asString
|
||||||
|
|> Just
|
||||||
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
Nothing
|
Nothing
|
||||||
@ -361,14 +443,8 @@ update sett msg model =
|
|||||||
updatePatternModel pm str
|
updatePatternModel pm str
|
||||||
|
|
||||||
newSettings =
|
newSettings =
|
||||||
if pm_.pattern /= Just sett.cardTitleTemplate.pattern then
|
if pm_.pattern /= sett.cardTitleTemplate then
|
||||||
Just
|
Just { sett | cardTitleTemplate = pm_.pattern }
|
||||||
{ sett
|
|
||||||
| cardTitleTemplate =
|
|
||||||
ItemPattern
|
|
||||||
(Maybe.withDefault "" pm_.pattern)
|
|
||||||
pm_.current
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
else
|
||||||
Nothing
|
Nothing
|
||||||
@ -384,14 +460,8 @@ update sett msg model =
|
|||||||
updatePatternModel pm str
|
updatePatternModel pm str
|
||||||
|
|
||||||
newSettings =
|
newSettings =
|
||||||
if pm_.pattern /= Just sett.cardSubtitleTemplate.pattern then
|
if pm_.pattern /= sett.cardSubtitleTemplate then
|
||||||
Just
|
Just { sett | cardSubtitleTemplate = pm_.pattern }
|
||||||
{ sett
|
|
||||||
| cardSubtitleTemplate =
|
|
||||||
ItemPattern
|
|
||||||
(Maybe.withDefault "" pm_.pattern)
|
|
||||||
pm_.current
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
else
|
||||||
Nothing
|
Nothing
|
||||||
@ -407,9 +477,21 @@ update sett msg model =
|
|||||||
not model.searchStatsVisible
|
not model.searchStatsVisible
|
||||||
in
|
in
|
||||||
( { model | searchStatsVisible = flag }
|
( { 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 ->
|
ToggleAkkordionTab name ->
|
||||||
let
|
let
|
||||||
tabs =
|
tabs =
|
||||||
@ -429,7 +511,7 @@ update sett msg model =
|
|||||||
not model.sideMenuVisible
|
not model.sideMenuVisible
|
||||||
in
|
in
|
||||||
( { model | sideMenuVisible = next }
|
( { model | sideMenuVisible = next }
|
||||||
, Just { sett | sideMenuVisible = next }
|
, Just { sett | sideMenuVisible = Just next }
|
||||||
)
|
)
|
||||||
|
|
||||||
TogglePowerSearch ->
|
TogglePowerSearch ->
|
||||||
@ -438,7 +520,7 @@ update sett msg model =
|
|||||||
not model.powerSearchEnabled
|
not model.powerSearchEnabled
|
||||||
in
|
in
|
||||||
( { model | powerSearchEnabled = next }
|
( { model | powerSearchEnabled = next }
|
||||||
, Just { sett | powerSearchEnabled = next }
|
, Just { sett | powerSearchEnabled = Just next }
|
||||||
)
|
)
|
||||||
|
|
||||||
UiLangMsg lm ->
|
UiLangMsg lm ->
|
||||||
@ -454,7 +536,7 @@ update sett msg model =
|
|||||||
Nothing
|
Nothing
|
||||||
|
|
||||||
else
|
else
|
||||||
Just { sett | uiLang = newLang }
|
Just { sett | uiLang = Just (Messages.toIso2 newLang) }
|
||||||
)
|
)
|
||||||
|
|
||||||
PdfModeMsg lm ->
|
PdfModeMsg lm ->
|
||||||
@ -470,9 +552,52 @@ update sett msg model =
|
|||||||
Nothing
|
Nothing
|
||||||
|
|
||||||
else
|
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
|
--- 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 =
|
view2 texts flags settings model =
|
||||||
let
|
let
|
||||||
state tab =
|
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 =
|
settingFormTabs texts flags _ model =
|
||||||
let
|
let
|
||||||
langCfg =
|
langCfg =
|
||||||
@ -533,10 +658,21 @@ settingFormTabs texts flags _ model =
|
|||||||
, style = DS.mainStyle
|
, style = DS.mainStyle
|
||||||
, selectPlaceholder = texts.basics.selectPlaceholder
|
, 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
|
in
|
||||||
[ { name = "general"
|
[ { name = akkordionTabName GeneralTab
|
||||||
, title = texts.general
|
, title = texts.general
|
||||||
, titleRight = []
|
, titleRight = [ resetLink GeneralTab ]
|
||||||
, info = Nothing
|
, info = Nothing
|
||||||
, body =
|
, body =
|
||||||
[ div [ class "mb-4 " ]
|
[ div [ class "mb-4 " ]
|
||||||
@ -560,9 +696,9 @@ settingFormTabs texts flags _ model =
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
, { name = "item-search"
|
, { name = akkordionTabName SearchTab
|
||||||
, title = texts.itemSearch
|
, title = texts.itemSearch
|
||||||
, titleRight = []
|
, titleRight = [ resetLink SearchTab ]
|
||||||
, info = Nothing
|
, info = Nothing
|
||||||
, body =
|
, body =
|
||||||
[ Html.map SearchPageSizeMsg
|
[ Html.map SearchPageSizeMsg
|
||||||
@ -594,9 +730,9 @@ settingFormTabs texts flags _ model =
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
, { name = "item-cards"
|
, { name = akkordionTabName CardsTab
|
||||||
, title = texts.itemCards
|
, title = texts.itemCards
|
||||||
, titleRight = []
|
, titleRight = [ resetLink CardsTab ]
|
||||||
, info = Nothing
|
, info = Nothing
|
||||||
, body =
|
, body =
|
||||||
[ Html.map NoteLengthMsg
|
[ Html.map NoteLengthMsg
|
||||||
@ -666,9 +802,9 @@ settingFormTabs texts flags _ model =
|
|||||||
texts.templateHelpMessage
|
texts.templateHelpMessage
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
, { name = "search-menu"
|
, { name = akkordionTabName SearchMenuTab
|
||||||
, title = texts.searchMenu
|
, title = texts.searchMenu
|
||||||
, titleRight = []
|
, titleRight = [ resetLink SearchMenuTab ]
|
||||||
, info = Nothing
|
, info = Nothing
|
||||||
, body =
|
, body =
|
||||||
[ Html.map SearchMenuTagMsg
|
[ Html.map SearchMenuTagMsg
|
||||||
@ -700,9 +836,9 @@ settingFormTabs texts flags _ model =
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
, { name = "item-detail"
|
, { name = akkordionTabName DetailTab
|
||||||
, title = texts.itemDetail
|
, title = texts.itemDetail
|
||||||
, titleRight = []
|
, titleRight = [ resetLink DetailTab ]
|
||||||
, info = Nothing
|
, info = Nothing
|
||||||
, body =
|
, body =
|
||||||
[ div [ class "mb-4" ]
|
[ div [ class "mb-4" ]
|
||||||
@ -726,9 +862,9 @@ settingFormTabs texts flags _ model =
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
, { name = "tag-category-colors"
|
, { name = akkordionTabName TagsTab
|
||||||
, title = texts.tagCategoryColors
|
, title = texts.tagCategoryColors
|
||||||
, titleRight = []
|
, titleRight = [ resetLink TagsTab ]
|
||||||
, info = Nothing
|
, info = Nothing
|
||||||
, body =
|
, body =
|
||||||
[ Html.map TagColorMsg
|
[ Html.map TagColorMsg
|
||||||
@ -739,9 +875,9 @@ settingFormTabs texts flags _ model =
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
, { name = "fields"
|
, { name = akkordionTabName FieldsTab
|
||||||
, title = texts.fields
|
, title = texts.fields
|
||||||
, titleRight = []
|
, titleRight = [ resetLink FieldsTab ]
|
||||||
, info = Nothing
|
, info = Nothing
|
||||||
, body =
|
, body =
|
||||||
[ span [ class "opacity-50 text-sm" ]
|
[ span [ class "opacity-50 text-sm" ]
|
||||||
|
@ -16,26 +16,44 @@ module Comp.UiSettingsManage exposing
|
|||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.BasicResult exposing (BasicResult)
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
|
import Comp.Basic
|
||||||
import Comp.MenuBar as MB
|
import Comp.MenuBar as MB
|
||||||
import Comp.UiSettingsForm
|
import Comp.UiSettingsForm
|
||||||
import Comp.UiSettingsMigrate
|
import Data.AccountScope exposing (AccountScope)
|
||||||
|
import Data.AppEvent exposing (AppEvent(..))
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.UiSettings exposing (StoredUiSettings, UiSettings)
|
import Data.UiSettings exposing (StoredUiSettings, UiSettings)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Http
|
import Http
|
||||||
import Messages.Comp.UiSettingsManage exposing (Texts)
|
import Messages.Comp.UiSettingsManage exposing (Texts)
|
||||||
|
import Page.Search.Data exposing (Msg(..))
|
||||||
|
import Process
|
||||||
import Styles as S
|
import Styles as S
|
||||||
|
import Task
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
{ formModel : Comp.UiSettingsForm.Model
|
{ formModel : FormView
|
||||||
, settings : Maybe UiSettings
|
|
||||||
, formResult : FormResult
|
, 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
|
type FormResult
|
||||||
= FormInit
|
= FormInit
|
||||||
| FormUnchanged
|
| FormUnchanged
|
||||||
@ -45,35 +63,40 @@ type FormResult
|
|||||||
|
|
||||||
|
|
||||||
type Msg
|
type Msg
|
||||||
= UiSettingsFormMsg Comp.UiSettingsForm.Msg
|
= UiFormMsg AccountScope Comp.UiSettingsForm.Msg
|
||||||
| UiSettingsMigrateMsg Comp.UiSettingsMigrate.Msg
|
|
||||||
| Submit
|
| Submit
|
||||||
| UpdateSettings
|
| SaveSettingsResp (Result Http.Error BasicResult)
|
||||||
| SaveSettingsResp UiSettings (Result Http.Error BasicResult)
|
| ReceiveServerSettings (Result Http.Error ( StoredUiSettings, StoredUiSettings ))
|
||||||
| ReceiveBrowserSettings StoredUiSettings
|
| ToggleExpandCollapse
|
||||||
|
| SwitchForm AccountScope
|
||||||
|
| ResetFormState
|
||||||
|
|
||||||
|
|
||||||
init : Flags -> UiSettings -> ( Model, Cmd Msg )
|
init : Flags -> ( Model, Cmd Msg )
|
||||||
init flags settings =
|
init flags =
|
||||||
let
|
( { formModel = ViewLoading
|
||||||
( fm, fc ) =
|
, formData = Nothing
|
||||||
Comp.UiSettingsForm.init flags settings
|
|
||||||
|
|
||||||
( mm, mc ) =
|
|
||||||
Comp.UiSettingsMigrate.init flags
|
|
||||||
in
|
|
||||||
( { formModel = fm
|
|
||||||
, settings = Nothing
|
|
||||||
, formResult = FormInit
|
, formResult = FormInit
|
||||||
, settingsMigrate = mm
|
|
||||||
}
|
}
|
||||||
, Cmd.batch
|
, Cmd.batch
|
||||||
[ Cmd.map UiSettingsFormMsg fc
|
[ Api.getClientSettingsRaw flags ReceiveServerSettings
|
||||||
, Cmd.map UiSettingsMigrateMsg mc
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
getViewScope : Model -> AccountScope
|
||||||
|
getViewScope model =
|
||||||
|
case model.formModel of
|
||||||
|
ViewCollective ->
|
||||||
|
Data.AccountScope.Collective
|
||||||
|
|
||||||
|
ViewUser ->
|
||||||
|
Data.AccountScope.User
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Data.AccountScope.User
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- update
|
--- update
|
||||||
|
|
||||||
@ -82,108 +105,177 @@ type alias UpdateResult =
|
|||||||
{ model : Model
|
{ model : Model
|
||||||
, cmd : Cmd Msg
|
, cmd : Cmd Msg
|
||||||
, sub : Sub 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 -> UiSettings -> Msg -> Model -> UpdateResult
|
||||||
update flags settings msg model =
|
update flags settings msg model =
|
||||||
case msg of
|
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
|
let
|
||||||
inSettings =
|
collDefaults =
|
||||||
Maybe.withDefault settings model.settings
|
Data.UiSettings.defaults
|
||||||
|
|
||||||
( m_, sett ) =
|
userDefaults =
|
||||||
Comp.UiSettingsForm.update inSettings lm model.formModel
|
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
|
in
|
||||||
{ model =
|
UpdateResult model_ cmds Sub.none AppNothing
|
||||||
{ model
|
|
||||||
| formModel = m_
|
|
||||||
, settings =
|
|
||||||
if sett == Nothing then
|
|
||||||
model.settings
|
|
||||||
|
|
||||||
else
|
ReceiveServerSettings (Err err) ->
|
||||||
sett
|
unit { model | formResult = FormHttpError err }
|
||||||
, formResult =
|
|
||||||
if sett /= Nothing then
|
|
||||||
FormInit
|
|
||||||
|
|
||||||
else
|
ToggleExpandCollapse ->
|
||||||
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 ->
|
|
||||||
let
|
let
|
||||||
lm =
|
lm =
|
||||||
UiSettingsMigrateMsg (Comp.UiSettingsMigrate.receiveBrowserSettings sett)
|
UiFormMsg (getViewScope model) Comp.UiSettingsForm.toggleAllTabs
|
||||||
in
|
in
|
||||||
update flags settings lm model
|
update flags settings lm model
|
||||||
|
|
||||||
Submit ->
|
SwitchForm scope ->
|
||||||
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 ->
|
|
||||||
let
|
let
|
||||||
( fm, fc ) =
|
forUser =
|
||||||
Comp.UiSettingsForm.init flags settings
|
unit { model | formModel = ViewUser }
|
||||||
|
|
||||||
|
forColl =
|
||||||
|
unit { model | formModel = ViewCollective }
|
||||||
in
|
in
|
||||||
{ model = { model | formModel = fm }
|
Data.AccountScope.fold forUser forColl scope
|
||||||
, cmd = Cmd.map UiSettingsFormMsg fc
|
|
||||||
, sub = Sub.none
|
|
||||||
, newSettings = Nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
|
ResetFormState ->
|
||||||
|
case model.formResult of
|
||||||
|
FormSaved ->
|
||||||
|
unit { model | formResult = FormInit }
|
||||||
|
|
||||||
|
_ ->
|
||||||
--- View2
|
unit model
|
||||||
|
|
||||||
|
|
||||||
isError : Model -> Bool
|
isError : Model -> Bool
|
||||||
@ -211,7 +303,11 @@ isSuccess model =
|
|||||||
|
|
||||||
|
|
||||||
view2 : Texts -> Flags -> UiSettings -> String -> Model -> Html Msg
|
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 ]
|
div [ class classes ]
|
||||||
[ MB.view
|
[ MB.view
|
||||||
{ start =
|
{ start =
|
||||||
@ -221,14 +317,29 @@ view2 texts flags settings classes model =
|
|||||||
, title = texts.saveSettings
|
, title = texts.saveSettings
|
||||||
, icon = Just "fa fa-save"
|
, 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"
|
, rootClasses = "mb-4"
|
||||||
}
|
}
|
||||||
, div []
|
|
||||||
[ Html.map UiSettingsMigrateMsg
|
|
||||||
(Comp.UiSettingsMigrate.view model.settingsMigrate)
|
|
||||||
]
|
|
||||||
, div
|
, div
|
||||||
[ classList
|
[ classList
|
||||||
[ ( S.successMessage, isSuccess model )
|
[ ( S.successMessage, isSuccess model )
|
||||||
@ -252,11 +363,55 @@ view2 texts flags settings classes model =
|
|||||||
FormUnknownError ->
|
FormUnknownError ->
|
||||||
text texts.unknownSaveError
|
text texts.unknownSaveError
|
||||||
]
|
]
|
||||||
, Html.map UiSettingsFormMsg
|
, case model.formModel of
|
||||||
(Comp.UiSettingsForm.view2
|
ViewLoading ->
|
||||||
texts.uiSettingsForm
|
div [ class "h-24 md:relative" ]
|
||||||
flags
|
[ Comp.Basic.loadingDimmer
|
||||||
settings
|
{ label = ""
|
||||||
model.formModel
|
, 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" ] []
|
||||||
]
|
]
|
||||||
|
@ -17,6 +17,7 @@ module Comp.UiSettingsMigrate exposing
|
|||||||
|
|
||||||
import Api
|
import Api
|
||||||
import Api.Model.BasicResult exposing (BasicResult)
|
import Api.Model.BasicResult exposing (BasicResult)
|
||||||
|
import Data.AccountScope
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.UiSettings exposing (StoredUiSettings, UiSettings)
|
import Data.UiSettings exposing (StoredUiSettings, UiSettings)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
@ -132,7 +133,10 @@ update flags msg model =
|
|||||||
Data.UiSettings.merge settings Data.UiSettings.defaults
|
Data.UiSettings.merge settings Data.UiSettings.defaults
|
||||||
|
|
||||||
cmd =
|
cmd =
|
||||||
Api.saveClientSettings flags uiSettings (SaveSettingsResp uiSettings)
|
Api.saveClientSettings flags
|
||||||
|
(Data.UiSettings.convert uiSettings)
|
||||||
|
Data.AccountScope.Collective
|
||||||
|
(SaveSettingsResp uiSettings)
|
||||||
in
|
in
|
||||||
{ empty | model = MigrateRequestRunning, cmd = cmd }
|
{ empty | model = MigrateRequestRunning, cmd = cmd }
|
||||||
|
|
||||||
|
@ -23,6 +23,11 @@ fold user coll scope =
|
|||||||
coll
|
coll
|
||||||
|
|
||||||
|
|
||||||
|
all : List AccountScope
|
||||||
|
all =
|
||||||
|
[ Collective, User ]
|
||||||
|
|
||||||
|
|
||||||
isUser : AccountScope -> Bool
|
isUser : AccountScope -> Bool
|
||||||
isUser scope =
|
isUser scope =
|
||||||
fold True False scope
|
fold True False scope
|
||||||
|
13
modules/webapp/src/main/elm/Data/AppEvent.elm
Normal file
13
modules/webapp/src/main/elm/Data/AppEvent.elm
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{-
|
||||||
|
Copyright 2020 Eike K. & Contributors
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
-}
|
||||||
|
|
||||||
|
|
||||||
|
module Data.AppEvent exposing (AppEvent(..))
|
||||||
|
|
||||||
|
|
||||||
|
type AppEvent
|
||||||
|
= AppNothing
|
||||||
|
| AppReloadUiSettings
|
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
module Data.UiSettings exposing
|
module Data.UiSettings exposing
|
||||||
( ItemPattern
|
( ItemPattern
|
||||||
, Pos(..)
|
|
||||||
, StoredUiSettings
|
, StoredUiSettings
|
||||||
, UiSettings
|
, UiSettings
|
||||||
, cardPreviewSize
|
, cardPreviewSize
|
||||||
@ -15,22 +14,21 @@ module Data.UiSettings exposing
|
|||||||
, catColor
|
, catColor
|
||||||
, catColorFg2
|
, catColorFg2
|
||||||
, catColorString2
|
, catColorString2
|
||||||
|
, convert
|
||||||
, defaults
|
, defaults
|
||||||
, documentationSite
|
, documentationSite
|
||||||
|
, emptyStoredSettings
|
||||||
, fieldHidden
|
, fieldHidden
|
||||||
, fieldVisible
|
, fieldVisible
|
||||||
, getUiLanguage
|
, getUiLanguage
|
||||||
, merge
|
, merge
|
||||||
, mergeDefaults
|
, mergeDefaults
|
||||||
, pdfUrl
|
, pdfUrl
|
||||||
, posFromString
|
|
||||||
, posToString
|
|
||||||
, storedUiSettingsDecoder
|
, storedUiSettingsDecoder
|
||||||
, storedUiSettingsEncode
|
, storedUiSettingsEncode
|
||||||
, tagColor
|
, tagColor
|
||||||
, tagColorFg2
|
, tagColorFg2
|
||||||
, tagColorString2
|
, tagColorString2
|
||||||
, toStoredUiSettings
|
|
||||||
)
|
)
|
||||||
|
|
||||||
import Api.Model.Tag exposing (Tag)
|
import Api.Model.Tag exposing (Tag)
|
||||||
@ -62,31 +60,53 @@ force default settings.
|
|||||||
-}
|
-}
|
||||||
type alias StoredUiSettings =
|
type alias StoredUiSettings =
|
||||||
{ itemSearchPageSize : Maybe Int
|
{ itemSearchPageSize : Maybe Int
|
||||||
, tagCategoryColors : List ( String, String )
|
, tagCategoryColors : Maybe (List ( String, String ))
|
||||||
, pdfMode : Maybe String
|
, pdfMode : Maybe String
|
||||||
, itemSearchNoteLength : Maybe Int
|
, itemSearchNoteLength : Maybe Int
|
||||||
, itemDetailNotesPosition : Maybe String
|
|
||||||
, searchMenuFolderCount : Maybe Int
|
, searchMenuFolderCount : Maybe Int
|
||||||
, searchMenuTagCount : Maybe Int
|
, searchMenuTagCount : Maybe Int
|
||||||
, searchMenuTagCatCount : Maybe Int
|
, searchMenuTagCatCount : Maybe Int
|
||||||
, formFields : Maybe (List String)
|
, formFields : Maybe (List String)
|
||||||
, itemDetailShortcuts : Bool
|
, itemDetailShortcuts : Maybe Bool
|
||||||
, searchMenuVisible : Bool
|
|
||||||
, editMenuVisible : Bool
|
|
||||||
, cardPreviewSize : Maybe String
|
, cardPreviewSize : Maybe String
|
||||||
, cardTitleTemplate : Maybe String
|
, cardTitleTemplate : Maybe String
|
||||||
, cardSubtitleTemplate : Maybe String
|
, cardSubtitleTemplate : Maybe String
|
||||||
, searchStatsVisible : Bool
|
, searchStatsVisible : Maybe Bool
|
||||||
, cardPreviewFullWidth : Bool
|
, cardPreviewFullWidth : Maybe Bool
|
||||||
, uiTheme : Maybe String
|
, uiTheme : Maybe String
|
||||||
, sideMenuVisible : Bool
|
, sideMenuVisible : Maybe Bool
|
||||||
, powerSearchEnabled : Bool
|
, powerSearchEnabled : Maybe Bool
|
||||||
, uiLang : Maybe String
|
, uiLang : Maybe String
|
||||||
, itemSearchShowGroups : Bool
|
, itemSearchShowGroups : Maybe Bool
|
||||||
, itemSearchArrange : Maybe String
|
, 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 : Decode.Decoder StoredUiSettings
|
||||||
storedUiSettingsDecoder =
|
storedUiSettingsDecoder =
|
||||||
let
|
let
|
||||||
@ -96,66 +116,63 @@ storedUiSettingsDecoder =
|
|||||||
maybeString =
|
maybeString =
|
||||||
Decode.maybe Decode.string
|
Decode.maybe Decode.string
|
||||||
|
|
||||||
def =
|
maybeBool =
|
||||||
defaults
|
Decode.maybe Decode.bool
|
||||||
in
|
in
|
||||||
Decode.succeed StoredUiSettings
|
Decode.succeed StoredUiSettings
|
||||||
|> P.optional "itemSearchPageSize" maybeInt Nothing
|
|> 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 "pdfMode" maybeString Nothing
|
||||||
|> P.optional "itemSearchNoteLength" maybeInt Nothing
|
|> P.optional "itemSearchNoteLength" maybeInt Nothing
|
||||||
|> P.optional "itemDetailNotesPosition" maybeString Nothing
|
|
||||||
|> P.optional "searchMenuFolderCount" maybeInt Nothing
|
|> P.optional "searchMenuFolderCount" maybeInt Nothing
|
||||||
|> P.optional "searchMenuTagCount" maybeInt Nothing
|
|> P.optional "searchMenuTagCount" maybeInt Nothing
|
||||||
|> P.optional "searchMenuTagCatCount" maybeInt Nothing
|
|> P.optional "searchMenuTagCatCount" maybeInt Nothing
|
||||||
|> P.optional "formFields" (Decode.maybe <| Decode.list Decode.string) Nothing
|
|> P.optional "formFields" (Decode.maybe <| Decode.list Decode.string) Nothing
|
||||||
|> P.optional "itemDetailShortcuts" Decode.bool def.itemDetailShortcuts
|
|> P.optional "itemDetailShortcuts" maybeBool Nothing
|
||||||
|> P.optional "searchMenuVisible" Decode.bool def.searchMenuVisible
|
|
||||||
|> P.optional "editMenuVisible" Decode.bool def.editMenuVisible
|
|
||||||
|> P.optional "cardPreviewSize" maybeString Nothing
|
|> P.optional "cardPreviewSize" maybeString Nothing
|
||||||
|> P.optional "cardTitleTemplate" maybeString Nothing
|
|> P.optional "cardTitleTemplate" maybeString Nothing
|
||||||
|> P.optional "cardSubtitleTemplate" maybeString Nothing
|
|> P.optional "cardSubtitleTemplate" maybeString Nothing
|
||||||
|> P.optional "searchStatsVisible" Decode.bool def.searchStatsVisible
|
|> P.optional "searchStatsVisible" maybeBool Nothing
|
||||||
|> P.optional "cardPreviewFullWidth" Decode.bool def.cardPreviewFullWidth
|
|> P.optional "cardPreviewFullWidth" maybeBool Nothing
|
||||||
|> P.optional "uiTheme" maybeString Nothing
|
|> P.optional "uiTheme" maybeString Nothing
|
||||||
|> P.optional "sideMenuVisible" Decode.bool def.sideMenuVisible
|
|> P.optional "sideMenuVisible" maybeBool Nothing
|
||||||
|> P.optional "powerSearchEnabled" Decode.bool def.powerSearchEnabled
|
|> P.optional "powerSearchEnabled" maybeBool Nothing
|
||||||
|> P.optional "uiLang" maybeString Nothing
|
|> P.optional "uiLang" maybeString Nothing
|
||||||
|> P.optional "itemSearchShowGroups" Decode.bool def.itemSearchShowGroups
|
|> P.optional "itemSearchShowGroups" maybeBool Nothing
|
||||||
|> P.optional "itemSearchArrange" maybeString Nothing
|
|> P.optional "itemSearchArrange" maybeString Nothing
|
||||||
|
|
||||||
|
|
||||||
storedUiSettingsEncode : StoredUiSettings -> Encode.Value
|
storedUiSettingsEncode : StoredUiSettings -> Encode.Value
|
||||||
storedUiSettingsEncode value =
|
storedUiSettingsEncode value =
|
||||||
let
|
let
|
||||||
maybeEnc enca ma =
|
maybeEnc field enca ma =
|
||||||
Maybe.map enca ma |> Maybe.withDefault Encode.null
|
Maybe.map (\a -> ( field, enca a )) ma
|
||||||
in
|
in
|
||||||
Encode.object
|
Encode.object <|
|
||||||
[ ( "itemSearchPageSize", maybeEnc Encode.int value.itemSearchPageSize )
|
List.filterMap identity
|
||||||
, ( "tagCategoryColors", Encode.dict identity Encode.string (Dict.fromList value.tagCategoryColors) )
|
[ maybeEnc "itemSearchPageSize" Encode.int value.itemSearchPageSize
|
||||||
, ( "pdfMode", maybeEnc Encode.string value.pdfMode )
|
, maybeEnc "tagCategoryColors"
|
||||||
, ( "itemSearchNoteLength", maybeEnc Encode.int value.itemSearchNoteLength )
|
(Encode.dict identity Encode.string)
|
||||||
, ( "itemDetailNotesPosition", maybeEnc Encode.string value.itemDetailNotesPosition )
|
(Maybe.map Dict.fromList value.tagCategoryColors)
|
||||||
, ( "searchMenuFolderCount", maybeEnc Encode.int value.searchMenuFolderCount )
|
, maybeEnc "pdfMode" Encode.string value.pdfMode
|
||||||
, ( "searchMenuTagCount", maybeEnc Encode.int value.searchMenuTagCount )
|
, maybeEnc "itemSearchNoteLength" Encode.int value.itemSearchNoteLength
|
||||||
, ( "searchMenuTagCatCount", maybeEnc Encode.int value.searchMenuTagCatCount )
|
, maybeEnc "searchMenuFolderCount" Encode.int value.searchMenuFolderCount
|
||||||
, ( "formFields", maybeEnc (Encode.list Encode.string) value.formFields )
|
, maybeEnc "searchMenuTagCount" Encode.int value.searchMenuTagCount
|
||||||
, ( "itemDetailShortcuts", Encode.bool value.itemDetailShortcuts )
|
, maybeEnc "searchMenuTagCatCount" Encode.int value.searchMenuTagCatCount
|
||||||
, ( "searchMenuVisible", Encode.bool value.searchMenuVisible )
|
, maybeEnc "formFields" (Encode.list Encode.string) value.formFields
|
||||||
, ( "editMenuVisible", Encode.bool value.editMenuVisible )
|
, maybeEnc "itemDetailShortcuts" Encode.bool value.itemDetailShortcuts
|
||||||
, ( "cardPreviewSize", maybeEnc Encode.string value.cardPreviewSize )
|
, maybeEnc "cardPreviewSize" Encode.string value.cardPreviewSize
|
||||||
, ( "cardTitleTemplate", maybeEnc Encode.string value.cardTitleTemplate )
|
, maybeEnc "cardTitleTemplate" Encode.string value.cardTitleTemplate
|
||||||
, ( "cardSubtitleTemplate", maybeEnc Encode.string value.cardSubtitleTemplate )
|
, maybeEnc "cardSubtitleTemplate" Encode.string value.cardSubtitleTemplate
|
||||||
, ( "searchStatsVisible", Encode.bool value.searchStatsVisible )
|
, maybeEnc "searchStatsVisible" Encode.bool value.searchStatsVisible
|
||||||
, ( "cardPreviewFullWidth", Encode.bool value.cardPreviewFullWidth )
|
, maybeEnc "cardPreviewFullWidth" Encode.bool value.cardPreviewFullWidth
|
||||||
, ( "uiTheme", maybeEnc Encode.string value.uiTheme )
|
, maybeEnc "uiTheme" Encode.string value.uiTheme
|
||||||
, ( "sideMenuVisible", Encode.bool value.sideMenuVisible )
|
, maybeEnc "sideMenuVisible" Encode.bool value.sideMenuVisible
|
||||||
, ( "powerSearchEnabled", Encode.bool value.powerSearchEnabled )
|
, maybeEnc "powerSearchEnabled" Encode.bool value.powerSearchEnabled
|
||||||
, ( "uiLang", maybeEnc Encode.string value.uiLang )
|
, maybeEnc "uiLang" Encode.string value.uiLang
|
||||||
, ( "itemSearchShowGroups", Encode.bool value.itemSearchShowGroups )
|
, maybeEnc "itemSearchShowGroups" Encode.bool value.itemSearchShowGroups
|
||||||
, ( "itemSearchArrange", maybeEnc Encode.string value.itemSearchArrange )
|
, maybeEnc "itemSearchArrange" Encode.string value.itemSearchArrange
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
{-| Settings for the web ui. These fields are all mandatory, since
|
{-| Settings for the web ui. These fields are all mandatory, since
|
||||||
@ -171,14 +188,11 @@ type alias UiSettings =
|
|||||||
, tagCategoryColors : Dict String Color
|
, tagCategoryColors : Dict String Color
|
||||||
, pdfMode : PdfMode
|
, pdfMode : PdfMode
|
||||||
, itemSearchNoteLength : Int
|
, itemSearchNoteLength : Int
|
||||||
, itemDetailNotesPosition : Pos
|
|
||||||
, searchMenuFolderCount : Int
|
, searchMenuFolderCount : Int
|
||||||
, searchMenuTagCount : Int
|
, searchMenuTagCount : Int
|
||||||
, searchMenuTagCatCount : Int
|
, searchMenuTagCatCount : Int
|
||||||
, formFields : List Field
|
, formFields : List Field
|
||||||
, itemDetailShortcuts : Bool
|
, itemDetailShortcuts : Bool
|
||||||
, searchMenuVisible : Bool
|
|
||||||
, editMenuVisible : Bool
|
|
||||||
, cardPreviewSize : BasicSize
|
, cardPreviewSize : BasicSize
|
||||||
, cardTitleTemplate : ItemPattern
|
, cardTitleTemplate : ItemPattern
|
||||||
, cardSubtitleTemplate : ItemPattern
|
, cardSubtitleTemplate : ItemPattern
|
||||||
@ -205,48 +219,17 @@ readPattern str =
|
|||||||
|> Maybe.map (ItemPattern 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 : UiSettings
|
||||||
defaults =
|
defaults =
|
||||||
{ itemSearchPageSize = 60
|
{ itemSearchPageSize = 60
|
||||||
, tagCategoryColors = Dict.empty
|
, tagCategoryColors = Dict.empty
|
||||||
, pdfMode = Data.Pdf.Detect
|
, pdfMode = Data.Pdf.Detect
|
||||||
, itemSearchNoteLength = 0
|
, itemSearchNoteLength = 0
|
||||||
, itemDetailNotesPosition = Bottom
|
|
||||||
, searchMenuFolderCount = 3
|
, searchMenuFolderCount = 3
|
||||||
, searchMenuTagCount = 6
|
, searchMenuTagCount = 6
|
||||||
, searchMenuTagCatCount = 3
|
, searchMenuTagCatCount = 3
|
||||||
, formFields = Data.Fields.all
|
, formFields = Data.Fields.all
|
||||||
, itemDetailShortcuts = False
|
, itemDetailShortcuts = False
|
||||||
, searchMenuVisible = False
|
|
||||||
, editMenuVisible = False
|
|
||||||
, cardPreviewSize = Data.BasicSize.Medium
|
, cardPreviewSize = Data.BasicSize.Medium
|
||||||
, cardTitleTemplate =
|
, cardTitleTemplate =
|
||||||
{ template = Data.ItemTemplate.name
|
{ template = Data.ItemTemplate.name
|
||||||
@ -273,7 +256,8 @@ merge given fallback =
|
|||||||
choose given.itemSearchPageSize fallback.itemSearchPageSize
|
choose given.itemSearchPageSize fallback.itemSearchPageSize
|
||||||
, tagCategoryColors =
|
, tagCategoryColors =
|
||||||
Dict.union
|
Dict.union
|
||||||
(Dict.fromList given.tagCategoryColors
|
(Maybe.map Dict.fromList given.tagCategoryColors
|
||||||
|
|> Maybe.withDefault Dict.empty
|
||||||
|> Dict.map (\_ -> Data.Color.fromString)
|
|> Dict.map (\_ -> Data.Color.fromString)
|
||||||
|> Dict.filter (\_ -> \mc -> mc /= Nothing)
|
|> Dict.filter (\_ -> \mc -> mc /= Nothing)
|
||||||
|> Dict.map (\_ -> Maybe.withDefault Data.Color.Grey)
|
|> Dict.map (\_ -> Maybe.withDefault Data.Color.Grey)
|
||||||
@ -285,9 +269,6 @@ merge given fallback =
|
|||||||
|> Maybe.withDefault fallback.pdfMode
|
|> Maybe.withDefault fallback.pdfMode
|
||||||
, itemSearchNoteLength =
|
, itemSearchNoteLength =
|
||||||
choose given.itemSearchNoteLength fallback.itemSearchNoteLength
|
choose given.itemSearchNoteLength fallback.itemSearchNoteLength
|
||||||
, itemDetailNotesPosition =
|
|
||||||
choose (Maybe.andThen posFromString given.itemDetailNotesPosition)
|
|
||||||
fallback.itemDetailNotesPosition
|
|
||||||
, searchMenuFolderCount =
|
, searchMenuFolderCount =
|
||||||
choose given.searchMenuFolderCount
|
choose given.searchMenuFolderCount
|
||||||
fallback.searchMenuFolderCount
|
fallback.searchMenuFolderCount
|
||||||
@ -299,9 +280,7 @@ merge given fallback =
|
|||||||
choose
|
choose
|
||||||
(Maybe.map Data.Fields.fromList given.formFields)
|
(Maybe.map Data.Fields.fromList given.formFields)
|
||||||
fallback.formFields
|
fallback.formFields
|
||||||
, itemDetailShortcuts = given.itemDetailShortcuts
|
, itemDetailShortcuts = choose given.itemDetailShortcuts fallback.itemDetailShortcuts
|
||||||
, searchMenuVisible = given.searchMenuVisible
|
|
||||||
, editMenuVisible = given.editMenuVisible
|
|
||||||
, cardPreviewSize =
|
, cardPreviewSize =
|
||||||
given.cardPreviewSize
|
given.cardPreviewSize
|
||||||
|> Maybe.andThen Data.BasicSize.fromString
|
|> Maybe.andThen Data.BasicSize.fromString
|
||||||
@ -312,17 +291,17 @@ merge given fallback =
|
|||||||
, cardSubtitleTemplate =
|
, cardSubtitleTemplate =
|
||||||
Maybe.andThen readPattern given.cardSubtitleTemplate
|
Maybe.andThen readPattern given.cardSubtitleTemplate
|
||||||
|> Maybe.withDefault fallback.cardSubtitleTemplate
|
|> Maybe.withDefault fallback.cardSubtitleTemplate
|
||||||
, searchStatsVisible = given.searchStatsVisible
|
, searchStatsVisible = choose given.searchStatsVisible fallback.searchStatsVisible
|
||||||
, cardPreviewFullWidth = given.cardPreviewFullWidth
|
, cardPreviewFullWidth = choose given.cardPreviewFullWidth fallback.cardPreviewFullWidth
|
||||||
, uiTheme =
|
, uiTheme =
|
||||||
Maybe.andThen Data.UiTheme.fromString given.uiTheme
|
Maybe.andThen Data.UiTheme.fromString given.uiTheme
|
||||||
|> Maybe.withDefault fallback.uiTheme
|
|> Maybe.withDefault fallback.uiTheme
|
||||||
, sideMenuVisible = given.sideMenuVisible
|
, sideMenuVisible = choose given.sideMenuVisible fallback.sideMenuVisible
|
||||||
, powerSearchEnabled = given.powerSearchEnabled
|
, powerSearchEnabled = choose given.powerSearchEnabled fallback.powerSearchEnabled
|
||||||
, uiLang =
|
, uiLang =
|
||||||
Maybe.map Messages.fromIso2 given.uiLang
|
Maybe.map Messages.fromIso2 given.uiLang
|
||||||
|> Maybe.withDefault Messages.UiLanguage.English
|
|> Maybe.withDefault fallback.uiLang
|
||||||
, itemSearchShowGroups = given.itemSearchShowGroups
|
, itemSearchShowGroups = choose given.itemSearchShowGroups fallback.itemSearchShowGroups
|
||||||
, itemSearchArrange =
|
, itemSearchArrange =
|
||||||
Maybe.andThen Data.ItemArrange.fromString given.itemSearchArrange
|
Maybe.andThen Data.ItemArrange.fromString given.itemSearchArrange
|
||||||
|> Maybe.withDefault fallback.itemSearchArrange
|
|> Maybe.withDefault fallback.itemSearchArrange
|
||||||
@ -334,37 +313,35 @@ mergeDefaults given =
|
|||||||
merge given defaults
|
merge given defaults
|
||||||
|
|
||||||
|
|
||||||
toStoredUiSettings : UiSettings -> StoredUiSettings
|
convert : UiSettings -> StoredUiSettings
|
||||||
toStoredUiSettings settings =
|
convert settings =
|
||||||
{ itemSearchPageSize = Just settings.itemSearchPageSize
|
{ itemSearchPageSize = Just settings.itemSearchPageSize
|
||||||
, tagCategoryColors =
|
, tagCategoryColors =
|
||||||
Dict.map (\_ -> Data.Color.toString) settings.tagCategoryColors
|
Dict.map (\_ -> Data.Color.toString) settings.tagCategoryColors
|
||||||
|> Dict.toList
|
|> Dict.toList
|
||||||
|
|> Just
|
||||||
, pdfMode = Just (Data.Pdf.asString settings.pdfMode)
|
, pdfMode = Just (Data.Pdf.asString settings.pdfMode)
|
||||||
, itemSearchNoteLength = Just settings.itemSearchNoteLength
|
, itemSearchNoteLength = Just settings.itemSearchNoteLength
|
||||||
, itemDetailNotesPosition = Just (posToString settings.itemDetailNotesPosition)
|
|
||||||
, searchMenuFolderCount = Just settings.searchMenuFolderCount
|
, searchMenuFolderCount = Just settings.searchMenuFolderCount
|
||||||
, searchMenuTagCount = Just settings.searchMenuTagCount
|
, searchMenuTagCount = Just settings.searchMenuTagCount
|
||||||
, searchMenuTagCatCount = Just settings.searchMenuTagCatCount
|
, searchMenuTagCatCount = Just settings.searchMenuTagCatCount
|
||||||
, formFields =
|
, formFields =
|
||||||
List.map Data.Fields.toString settings.formFields
|
List.map Data.Fields.toString settings.formFields
|
||||||
|> Just
|
|> Just
|
||||||
, itemDetailShortcuts = settings.itemDetailShortcuts
|
, itemDetailShortcuts = Just settings.itemDetailShortcuts
|
||||||
, searchMenuVisible = settings.searchMenuVisible
|
|
||||||
, editMenuVisible = settings.editMenuVisible
|
|
||||||
, cardPreviewSize =
|
, cardPreviewSize =
|
||||||
settings.cardPreviewSize
|
settings.cardPreviewSize
|
||||||
|> Data.BasicSize.asString
|
|> Data.BasicSize.asString
|
||||||
|> Just
|
|> Just
|
||||||
, cardTitleTemplate = settings.cardTitleTemplate.pattern |> Just
|
, cardTitleTemplate = settings.cardTitleTemplate.pattern |> Just
|
||||||
, cardSubtitleTemplate = settings.cardSubtitleTemplate.pattern |> Just
|
, cardSubtitleTemplate = settings.cardSubtitleTemplate.pattern |> Just
|
||||||
, searchStatsVisible = settings.searchStatsVisible
|
, searchStatsVisible = Just settings.searchStatsVisible
|
||||||
, cardPreviewFullWidth = settings.cardPreviewFullWidth
|
, cardPreviewFullWidth = Just settings.cardPreviewFullWidth
|
||||||
, uiTheme = Just (Data.UiTheme.toString settings.uiTheme)
|
, uiTheme = Just (Data.UiTheme.toString settings.uiTheme)
|
||||||
, sideMenuVisible = settings.sideMenuVisible
|
, sideMenuVisible = Just settings.sideMenuVisible
|
||||||
, powerSearchEnabled = settings.powerSearchEnabled
|
, powerSearchEnabled = Just settings.powerSearchEnabled
|
||||||
, uiLang = Just <| Messages.toIso2 settings.uiLang
|
, uiLang = Just <| Messages.toIso2 settings.uiLang
|
||||||
, itemSearchShowGroups = settings.itemSearchShowGroups
|
, itemSearchShowGroups = Just settings.itemSearchShowGroups
|
||||||
, itemSearchArrange = Data.ItemArrange.asString settings.itemSearchArrange |> Just
|
, itemSearchArrange = Data.ItemArrange.asString settings.itemSearchArrange |> Just
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ import App.View2
|
|||||||
import Browser exposing (Document)
|
import Browser exposing (Document)
|
||||||
import Browser.Navigation exposing (Key)
|
import Browser.Navigation exposing (Key)
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.NotificationChannel
|
|
||||||
import Data.UiSettings
|
import Data.UiSettings
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
@ -93,6 +92,5 @@ subscriptions : Model -> Sub Msg
|
|||||||
subscriptions model =
|
subscriptions model =
|
||||||
Sub.batch
|
Sub.batch
|
||||||
[ model.subs
|
[ model.subs
|
||||||
, Ports.receiveUiSettings ReceiveBrowserSettings
|
|
||||||
, Ports.receiveServerEvent ReceiveWsMessage
|
, Ports.receiveServerEvent ReceiveWsMessage
|
||||||
]
|
]
|
||||||
|
@ -56,6 +56,7 @@ type alias Texts =
|
|||||||
, fieldLabel : Field -> String
|
, fieldLabel : Field -> String
|
||||||
, templateHelpMessage : String
|
, templateHelpMessage : String
|
||||||
, pdfMode : PdfMode -> 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.
|
is rendered.
|
||||||
"""
|
"""
|
||||||
, pdfMode = Messages.Data.PdfMode.gb
|
, pdfMode = Messages.Data.PdfMode.gb
|
||||||
|
, resetLabel = "Reset"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -208,4 +210,5 @@ oder, wenn diese leer ist, die Person. Sind beide leer wird ein `-`
|
|||||||
dargestellt.
|
dargestellt.
|
||||||
"""
|
"""
|
||||||
, pdfMode = Messages.Data.PdfMode.de
|
, pdfMode = Messages.Data.PdfMode.de
|
||||||
|
, resetLabel = "Zurücksetzen"
|
||||||
}
|
}
|
||||||
|
@ -15,16 +15,23 @@ import Http
|
|||||||
import Messages.Basics
|
import Messages.Basics
|
||||||
import Messages.Comp.HttpError
|
import Messages.Comp.HttpError
|
||||||
import Messages.Comp.UiSettingsForm
|
import Messages.Comp.UiSettingsForm
|
||||||
|
import Messages.Data.AccountScope
|
||||||
|
|
||||||
|
|
||||||
type alias Texts =
|
type alias Texts =
|
||||||
{ basics : Messages.Basics.Texts
|
{ basics : Messages.Basics.Texts
|
||||||
, uiSettingsForm : Messages.Comp.UiSettingsForm.Texts
|
, uiSettingsForm : Messages.Comp.UiSettingsForm.Texts
|
||||||
|
, accountScope : Messages.Data.AccountScope.Texts
|
||||||
, saveSettings : String
|
, saveSettings : String
|
||||||
, settingsUnchanged : String
|
, settingsUnchanged : String
|
||||||
, settingsSaved : String
|
, settingsSaved : String
|
||||||
, unknownSaveError : String
|
, unknownSaveError : String
|
||||||
, httpError : Http.Error -> String
|
, httpError : Http.Error -> String
|
||||||
|
, userHeader : String
|
||||||
|
, userInfo : String
|
||||||
|
, collectiveHeader : String
|
||||||
|
, collectiveInfo : String
|
||||||
|
, expandCollapse : String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -32,11 +39,17 @@ gb : Texts
|
|||||||
gb =
|
gb =
|
||||||
{ basics = Messages.Basics.gb
|
{ basics = Messages.Basics.gb
|
||||||
, uiSettingsForm = Messages.Comp.UiSettingsForm.gb
|
, uiSettingsForm = Messages.Comp.UiSettingsForm.gb
|
||||||
|
, accountScope = Messages.Data.AccountScope.gb
|
||||||
, saveSettings = "Save settings"
|
, saveSettings = "Save settings"
|
||||||
, settingsUnchanged = "Settings unchanged or invalid."
|
, settingsUnchanged = "Settings unchanged or invalid."
|
||||||
, settingsSaved = "Settings saved."
|
, settingsSaved = "Settings saved."
|
||||||
, unknownSaveError = "Unknown error while trying to save settings."
|
, unknownSaveError = "Unknown error while trying to save settings."
|
||||||
, httpError = Messages.Comp.HttpError.gb
|
, 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 =
|
de =
|
||||||
{ basics = Messages.Basics.de
|
{ basics = Messages.Basics.de
|
||||||
, uiSettingsForm = Messages.Comp.UiSettingsForm.de
|
, uiSettingsForm = Messages.Comp.UiSettingsForm.de
|
||||||
|
, accountScope = Messages.Data.AccountScope.de
|
||||||
, saveSettings = "Einstellungen speichern"
|
, saveSettings = "Einstellungen speichern"
|
||||||
, settingsUnchanged = "Einstellungen nicht verändert oder ungültig."
|
, settingsUnchanged = "Einstellungen nicht verändert oder ungültig."
|
||||||
, settingsSaved = "Einstellungen gespeichert"
|
, settingsSaved = "Einstellungen gespeichert"
|
||||||
, unknownSaveError = "Unbekannter Fehler beim Speichern der Einstellungen."
|
, unknownSaveError = "Unbekannter Fehler beim Speichern der Einstellungen."
|
||||||
, httpError = Messages.Comp.HttpError.de
|
, 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"
|
||||||
}
|
}
|
||||||
|
@ -80,8 +80,7 @@ gb =
|
|||||||
, changePassword = "Change Password"
|
, changePassword = "Change Password"
|
||||||
, channelSettings = "Notification Channels"
|
, channelSettings = "Notification Channels"
|
||||||
, uiSettingsInfo =
|
, uiSettingsInfo =
|
||||||
"These settings only affect the web ui. They are stored in the browser, "
|
"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."
|
||||||
++ "so they are separated between browsers and devices."
|
|
||||||
, scanMailboxInfo1 =
|
, scanMailboxInfo1 =
|
||||||
"Docspell can scan folders of your mailbox to import your mails. "
|
"Docspell can scan folders of your mailbox to import your mails. "
|
||||||
++ "You need to provide a connection in "
|
++ "You need to provide a connection in "
|
||||||
@ -144,7 +143,7 @@ de =
|
|||||||
, channelSettings = "Benachrichtigungskanäle"
|
, channelSettings = "Benachrichtigungskanäle"
|
||||||
, changePassword = "Passwort ändern"
|
, changePassword = "Passwort ändern"
|
||||||
, uiSettingsInfo =
|
, 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 =
|
, scanMailboxInfo1 =
|
||||||
"""Docspell kann Postfächer durchsuchen und E-Mails importieren. Dafür sind
|
"""Docspell kann Postfächer durchsuchen und E-Mails importieren. Dafür sind
|
||||||
E-Mail-Einstellungen (IMAP) notwendig."""
|
E-Mail-Einstellungen (IMAP) notwendig."""
|
||||||
|
@ -109,8 +109,7 @@ initSelectViewModel flags =
|
|||||||
|
|
||||||
|
|
||||||
type ViewMode
|
type ViewMode
|
||||||
= SimpleView
|
= SearchView
|
||||||
| SearchView
|
|
||||||
| SelectView SelectViewModel
|
| SelectView SelectViewModel
|
||||||
| PublishView Comp.PublishItems.Model
|
| PublishView Comp.PublishItems.Model
|
||||||
|
|
||||||
@ -149,9 +148,6 @@ init flags viewMode =
|
|||||||
menuCollapsed : Model -> Bool
|
menuCollapsed : Model -> Bool
|
||||||
menuCollapsed model =
|
menuCollapsed model =
|
||||||
case model.viewMode of
|
case model.viewMode of
|
||||||
SimpleView ->
|
|
||||||
True
|
|
||||||
|
|
||||||
SearchView ->
|
SearchView ->
|
||||||
False
|
False
|
||||||
|
|
||||||
@ -165,9 +161,6 @@ menuCollapsed model =
|
|||||||
selectActive : Model -> Bool
|
selectActive : Model -> Bool
|
||||||
selectActive model =
|
selectActive model =
|
||||||
case model.viewMode of
|
case model.viewMode of
|
||||||
SimpleView ->
|
|
||||||
False
|
|
||||||
|
|
||||||
SearchView ->
|
SearchView ->
|
||||||
False
|
False
|
||||||
|
|
||||||
@ -181,9 +174,6 @@ selectActive model =
|
|||||||
editActive : Model -> Bool
|
editActive : Model -> Bool
|
||||||
editActive model =
|
editActive model =
|
||||||
case model.viewMode of
|
case model.viewMode of
|
||||||
SimpleView ->
|
|
||||||
False
|
|
||||||
|
|
||||||
SearchView ->
|
SearchView ->
|
||||||
False
|
False
|
||||||
|
|
||||||
@ -203,7 +193,6 @@ type Msg
|
|||||||
| ItemSearchResp Bool (Result Http.Error ItemLightList)
|
| ItemSearchResp Bool (Result Http.Error ItemLightList)
|
||||||
| ItemSearchAddResp (Result Http.Error ItemLightList)
|
| ItemSearchAddResp (Result Http.Error ItemLightList)
|
||||||
| DoSearch SearchType
|
| DoSearch SearchType
|
||||||
| ToggleSearchMenu
|
|
||||||
| ToggleSelectView
|
| ToggleSelectView
|
||||||
| LoadMore
|
| LoadMore
|
||||||
| SetBasicSearch String
|
| SetBasicSearch String
|
||||||
@ -231,7 +220,7 @@ type Msg
|
|||||||
| KeyUpPowerSearchbarMsg (Maybe KeyCode)
|
| KeyUpPowerSearchbarMsg (Maybe KeyCode)
|
||||||
| RequestReprocessSelected
|
| RequestReprocessSelected
|
||||||
| ReprocessSelectedConfirmed
|
| ReprocessSelectedConfirmed
|
||||||
| ClientSettingsSaveResp UiSettings (Result Http.Error BasicResult)
|
| ClientSettingsSaveResp (Result Http.Error BasicResult)
|
||||||
| RemoveItem String
|
| RemoveItem String
|
||||||
| MergeSelectedItems
|
| MergeSelectedItems
|
||||||
| MergeItemsMsg Comp.ItemMerge.Msg
|
| MergeItemsMsg Comp.ItemMerge.Msg
|
||||||
|
@ -22,7 +22,9 @@ import Comp.LinkTarget exposing (LinkTarget)
|
|||||||
import Comp.PowerSearchInput
|
import Comp.PowerSearchInput
|
||||||
import Comp.PublishItems
|
import Comp.PublishItems
|
||||||
import Comp.SearchMenu
|
import Comp.SearchMenu
|
||||||
|
import Data.AppEvent exposing (AppEvent(..))
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
|
import Data.ItemArrange
|
||||||
import Data.ItemQuery as Q
|
import Data.ItemQuery as Q
|
||||||
import Data.ItemSelection
|
import Data.ItemSelection
|
||||||
import Data.Items
|
import Data.Items
|
||||||
@ -44,7 +46,7 @@ type alias UpdateResult =
|
|||||||
{ model : Model
|
{ model : Model
|
||||||
, cmd : Cmd Msg
|
, cmd : Cmd Msg
|
||||||
, sub : Sub Msg
|
, sub : Sub Msg
|
||||||
, newSettings : Maybe UiSettings
|
, appEvent : AppEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -74,7 +76,7 @@ update bookmarkId mId key flags texts settings msg model =
|
|||||||
model
|
model
|
||||||
|
|
||||||
DoNothing ->
|
DoNothing ->
|
||||||
UpdateResult model Cmd.none Sub.none Nothing
|
UpdateResult model Cmd.none Sub.none AppNothing
|
||||||
|
|
||||||
ResetSearch ->
|
ResetSearch ->
|
||||||
let
|
let
|
||||||
@ -274,34 +276,10 @@ update bookmarkId mId key flags texts settings msg model =
|
|||||||
else
|
else
|
||||||
doSearch param model
|
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 ->
|
ToggleSelectView ->
|
||||||
let
|
let
|
||||||
( nextView, cmd ) =
|
( nextView, cmd ) =
|
||||||
case model.viewMode of
|
case model.viewMode of
|
||||||
SimpleView ->
|
|
||||||
( SelectView <| initSelectViewModel flags, loadEditModel flags )
|
|
||||||
|
|
||||||
SearchView ->
|
SearchView ->
|
||||||
( SelectView <| initSelectViewModel flags, loadEditModel flags )
|
( SelectView <| initSelectViewModel flags, loadEditModel flags )
|
||||||
|
|
||||||
@ -642,11 +620,7 @@ update bookmarkId mId key flags texts settings msg model =
|
|||||||
SelectView { svm | mergeModel = result.model }
|
SelectView { svm | mergeModel = result.model }
|
||||||
|
|
||||||
Comp.ItemMerge.OutcomeMerged ->
|
Comp.ItemMerge.OutcomeMerged ->
|
||||||
if settings.searchMenuVisible then
|
SearchView
|
||||||
SearchView
|
|
||||||
|
|
||||||
else
|
|
||||||
SimpleView
|
|
||||||
|
|
||||||
model_ =
|
model_ =
|
||||||
{ model | viewMode = nextView }
|
{ model | viewMode = nextView }
|
||||||
@ -834,17 +808,10 @@ update bookmarkId mId key flags texts settings msg model =
|
|||||||
UiSettingsUpdated ->
|
UiSettingsUpdated ->
|
||||||
let
|
let
|
||||||
defaultViewMode =
|
defaultViewMode =
|
||||||
if settings.searchMenuVisible then
|
SearchView
|
||||||
SearchView
|
|
||||||
|
|
||||||
else
|
|
||||||
SimpleView
|
|
||||||
|
|
||||||
viewMode =
|
viewMode =
|
||||||
case model.viewMode of
|
case model.viewMode of
|
||||||
SimpleView ->
|
|
||||||
defaultViewMode
|
|
||||||
|
|
||||||
SearchView ->
|
SearchView ->
|
||||||
defaultViewMode
|
defaultViewMode
|
||||||
|
|
||||||
@ -868,26 +835,26 @@ update bookmarkId mId key flags texts settings msg model =
|
|||||||
|
|
||||||
TogglePreviewFullWidth ->
|
TogglePreviewFullWidth ->
|
||||||
let
|
let
|
||||||
newSettings =
|
newSettings s =
|
||||||
{ settings | cardPreviewFullWidth = not settings.cardPreviewFullWidth }
|
{ s | cardPreviewFullWidth = Just (not settings.cardPreviewFullWidth) }
|
||||||
|
|
||||||
cmd =
|
cmd =
|
||||||
Api.saveClientSettings flags newSettings (ClientSettingsSaveResp newSettings)
|
Api.saveUserClientSettingsBy flags newSettings ClientSettingsSaveResp
|
||||||
in
|
in
|
||||||
noSub ( { model | viewMenuOpen = False }, cmd )
|
noSub ( { model | viewMenuOpen = False }, cmd )
|
||||||
|
|
||||||
ClientSettingsSaveResp newSettings (Ok res) ->
|
ClientSettingsSaveResp (Ok res) ->
|
||||||
if res.success then
|
if res.success then
|
||||||
{ model = model
|
{ model = model
|
||||||
, cmd = Cmd.none
|
, cmd = Cmd.none
|
||||||
, sub = Sub.none
|
, sub = Sub.none
|
||||||
, newSettings = Just newSettings
|
, appEvent = AppReloadUiSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
noSub ( model, Cmd.none )
|
noSub ( model, Cmd.none )
|
||||||
|
|
||||||
ClientSettingsSaveResp _ (Err _) ->
|
ClientSettingsSaveResp (Err _) ->
|
||||||
noSub ( model, Cmd.none )
|
noSub ( model, Cmd.none )
|
||||||
|
|
||||||
PowerSearchMsg lm ->
|
PowerSearchMsg lm ->
|
||||||
@ -1015,21 +982,21 @@ update bookmarkId mId key flags texts settings msg model =
|
|||||||
|
|
||||||
ToggleShowGroups ->
|
ToggleShowGroups ->
|
||||||
let
|
let
|
||||||
newSettings =
|
newSettings s =
|
||||||
{ settings | itemSearchShowGroups = not settings.itemSearchShowGroups }
|
{ s | itemSearchShowGroups = Just (not settings.itemSearchShowGroups) }
|
||||||
|
|
||||||
cmd =
|
cmd =
|
||||||
Api.saveClientSettings flags newSettings (ClientSettingsSaveResp newSettings)
|
Api.saveUserClientSettingsBy flags newSettings ClientSettingsSaveResp
|
||||||
in
|
in
|
||||||
noSub ( { model | viewMenuOpen = False }, cmd )
|
noSub ( { model | viewMenuOpen = False }, cmd )
|
||||||
|
|
||||||
ToggleArrange am ->
|
ToggleArrange am ->
|
||||||
let
|
let
|
||||||
newSettings =
|
newSettings s =
|
||||||
{ settings | itemSearchArrange = am }
|
{ s | itemSearchArrange = Data.ItemArrange.asString am |> Just }
|
||||||
|
|
||||||
cmd =
|
cmd =
|
||||||
Api.saveClientSettings flags newSettings (ClientSettingsSaveResp newSettings)
|
Api.saveUserClientSettingsBy flags newSettings ClientSettingsSaveResp
|
||||||
in
|
in
|
||||||
noSub ( { model | viewMenuOpen = False }, cmd )
|
noSub ( { model | viewMenuOpen = False }, cmd )
|
||||||
|
|
||||||
@ -1201,5 +1168,5 @@ makeResult ( m, c, s ) =
|
|||||||
{ model = m
|
{ model = m
|
||||||
, cmd = c
|
, cmd = c
|
||||||
, sub = s
|
, sub = s
|
||||||
, newSettings = Nothing
|
, appEvent = AppNothing
|
||||||
}
|
}
|
||||||
|
@ -93,9 +93,6 @@ mainView texts flags settings model =
|
|||||||
(publishResults texts settings flags model pm)
|
(publishResults texts settings flags model pm)
|
||||||
]
|
]
|
||||||
|
|
||||||
SimpleView ->
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
SearchView ->
|
SearchView ->
|
||||||
Nothing
|
Nothing
|
||||||
in
|
in
|
||||||
@ -109,7 +106,7 @@ mainView texts flags settings model =
|
|||||||
|
|
||||||
|
|
||||||
bookmarkQueryWidget : Texts -> UiSettings -> Flags -> Model -> List (Html Msg)
|
bookmarkQueryWidget : Texts -> UiSettings -> Flags -> Model -> List (Html Msg)
|
||||||
bookmarkQueryWidget texts settings flags model =
|
bookmarkQueryWidget texts _ _ model =
|
||||||
case model.topWidgetModel of
|
case model.topWidgetModel of
|
||||||
BookmarkQuery m ->
|
BookmarkQuery m ->
|
||||||
[ div [ class "px-2 mb-4 border-l border-r border-b dark:border-slate-600" ]
|
[ 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 -> UiSettings -> Flags -> Model -> Comp.PublishItems.Model -> List (Html Msg)
|
||||||
publishResults texts settings flags model pm =
|
publishResults texts settings flags _ pm =
|
||||||
[ Html.map PublishViewMsg
|
[ Html.map PublishViewMsg
|
||||||
(Comp.PublishItems.view texts.publishItems settings flags pm)
|
(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 -> UiSettings -> Model -> List (Html Msg)
|
||||||
itemsBar texts flags settings model =
|
itemsBar texts flags settings model =
|
||||||
case model.viewMode of
|
case model.viewMode of
|
||||||
SimpleView ->
|
|
||||||
[ defaultMenuBar texts flags settings model ]
|
|
||||||
|
|
||||||
SearchView ->
|
SearchView ->
|
||||||
[ defaultMenuBar texts flags settings model ]
|
[ defaultMenuBar texts flags settings model ]
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import Comp.SearchMenu
|
|||||||
import Comp.SharePasswordForm
|
import Comp.SharePasswordForm
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.ItemArrange exposing (ItemArrange)
|
import Data.ItemArrange exposing (ItemArrange)
|
||||||
|
import Data.UiSettings exposing (UiSettings)
|
||||||
import Http
|
import Http
|
||||||
import Page.Search.Data exposing (Msg(..))
|
import Page.Search.Data exposing (Msg(..))
|
||||||
import Set exposing (Set)
|
import Set exposing (Set)
|
||||||
@ -53,6 +54,7 @@ type alias Model =
|
|||||||
, initialized : Bool
|
, initialized : Bool
|
||||||
, contentSearch : Maybe String
|
, contentSearch : Maybe String
|
||||||
, searchMode : SearchBarMode
|
, searchMode : SearchBarMode
|
||||||
|
, uiSettings : UiSettings
|
||||||
, viewMode :
|
, viewMode :
|
||||||
{ menuOpen : Bool
|
{ menuOpen : Bool
|
||||||
, showGroups : Bool
|
, showGroups : Bool
|
||||||
@ -75,6 +77,7 @@ emptyModel flags =
|
|||||||
, initialized = False
|
, initialized = False
|
||||||
, contentSearch = Nothing
|
, contentSearch = Nothing
|
||||||
, searchMode = SearchBarContent
|
, searchMode = SearchBarContent
|
||||||
|
, uiSettings = Data.UiSettings.defaults
|
||||||
, viewMode =
|
, viewMode =
|
||||||
{ menuOpen = False
|
{ menuOpen = False
|
||||||
, showGroups = True
|
, showGroups = True
|
||||||
@ -107,6 +110,7 @@ type Msg
|
|||||||
= VerifyResp (Result Http.Error ShareVerifyResult)
|
= VerifyResp (Result Http.Error ShareVerifyResult)
|
||||||
| SearchResp (Result Http.Error ItemLightList)
|
| SearchResp (Result Http.Error ItemLightList)
|
||||||
| StatsResp Bool (Result Http.Error SearchStats)
|
| StatsResp Bool (Result Http.Error SearchStats)
|
||||||
|
| UiSettingsResp (Result Http.Error UiSettings)
|
||||||
| PasswordMsg Comp.SharePasswordForm.Msg
|
| PasswordMsg Comp.SharePasswordForm.Msg
|
||||||
| SearchMenuMsg Comp.SearchMenu.Msg
|
| SearchMenuMsg Comp.SearchMenu.Msg
|
||||||
| PowerSearchMsg Comp.PowerSearchInput.Msg
|
| PowerSearchMsg Comp.PowerSearchInput.Msg
|
||||||
|
@ -10,7 +10,6 @@ module Page.Share.Sidebar exposing (..)
|
|||||||
import Comp.SearchMenu
|
import Comp.SearchMenu
|
||||||
import Comp.Tabs
|
import Comp.Tabs
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Messages.Page.Share exposing (Texts)
|
import Messages.Page.Share exposing (Texts)
|
||||||
@ -18,8 +17,8 @@ import Page.Share.Data exposing (Model, Msg(..))
|
|||||||
import Util.ItemDragDrop
|
import Util.ItemDragDrop
|
||||||
|
|
||||||
|
|
||||||
view : Texts -> Flags -> UiSettings -> Model -> Html Msg
|
view : Texts -> Flags -> Model -> Html Msg
|
||||||
view texts flags settings model =
|
view texts flags model =
|
||||||
let
|
let
|
||||||
hideTrashTab tab default =
|
hideTrashTab tab default =
|
||||||
case tab of
|
case tab of
|
||||||
@ -41,7 +40,7 @@ view texts flags settings model =
|
|||||||
ddDummy
|
ddDummy
|
||||||
flags
|
flags
|
||||||
searchMenuCfg
|
searchMenuCfg
|
||||||
settings
|
model.uiSettings
|
||||||
model.searchMenuModel
|
model.searchMenuModel
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
@ -43,7 +43,10 @@ update flags settings shareId msg model =
|
|||||||
, verifyResult = res
|
, verifyResult = res
|
||||||
, searchInProgress = True
|
, searchInProgress = True
|
||||||
}
|
}
|
||||||
, makeSearchCmd flags True model
|
, Cmd.batch
|
||||||
|
[ makeSearchCmd flags True model
|
||||||
|
, Api.clientSettingsShare flags res.token UiSettingsResp
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
else if res.passwordRequired then
|
else if res.passwordRequired then
|
||||||
@ -242,6 +245,12 @@ update flags settings shareId msg model =
|
|||||||
in
|
in
|
||||||
noSub ( { model | viewMode = next }, Cmd.none )
|
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 : ( Model, Cmd Msg ) -> UpdateResult
|
||||||
noSub ( m, c ) =
|
noSub ( m, c ) =
|
||||||
|
@ -11,11 +11,9 @@ import Api.Model.VersionInfo exposing (VersionInfo)
|
|||||||
import Comp.Basic as B
|
import Comp.Basic as B
|
||||||
import Comp.SharePasswordForm
|
import Comp.SharePasswordForm
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.Items
|
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onInput, onSubmit)
|
|
||||||
import Messages.Page.Share exposing (Texts)
|
import Messages.Page.Share exposing (Texts)
|
||||||
import Page.Share.Data exposing (..)
|
import Page.Share.Data exposing (..)
|
||||||
import Page.Share.Menubar as Menubar
|
import Page.Share.Menubar as Menubar
|
||||||
@ -25,19 +23,19 @@ import Styles as S
|
|||||||
|
|
||||||
|
|
||||||
viewSidebar : Texts -> Bool -> Flags -> UiSettings -> Model -> Html Msg
|
viewSidebar : Texts -> Bool -> Flags -> UiSettings -> Model -> Html Msg
|
||||||
viewSidebar texts visible flags settings model =
|
viewSidebar texts visible flags _ model =
|
||||||
div
|
div
|
||||||
[ id "sidebar"
|
[ id "sidebar"
|
||||||
, class S.sidebar
|
, class S.sidebar
|
||||||
, class S.sidebarBg
|
, class S.sidebarBg
|
||||||
, classList [ ( "hidden", not visible || model.mode /= ModeShare ) ]
|
, 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 -> String -> Model -> Html Msg
|
||||||
viewContent texts flags versionInfo uiSettings shareId model =
|
viewContent texts flags versionInfo _ shareId model =
|
||||||
case model.mode of
|
case model.mode of
|
||||||
ModeInitial ->
|
ModeInitial ->
|
||||||
div
|
div
|
||||||
@ -60,15 +58,15 @@ viewContent texts flags versionInfo uiSettings shareId model =
|
|||||||
passwordContent texts flags versionInfo model
|
passwordContent texts flags versionInfo model
|
||||||
|
|
||||||
ModeShare ->
|
ModeShare ->
|
||||||
mainContent texts flags uiSettings shareId model
|
mainContent texts flags shareId model
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Helpers
|
--- Helpers
|
||||||
|
|
||||||
|
|
||||||
mainContent : Texts -> Flags -> UiSettings -> String -> Model -> Html Msg
|
mainContent : Texts -> Flags -> String -> Model -> Html Msg
|
||||||
mainContent texts flags settings shareId model =
|
mainContent texts flags shareId model =
|
||||||
div
|
div
|
||||||
[ id "content"
|
[ id "content"
|
||||||
, class "h-full flex flex-col"
|
, class "h-full flex flex-col"
|
||||||
@ -82,7 +80,7 @@ mainContent texts flags settings shareId model =
|
|||||||
]
|
]
|
||||||
, Menubar.view texts flags model
|
, Menubar.view texts flags model
|
||||||
, errorMessage texts model
|
, errorMessage texts model
|
||||||
, Results.view texts settings flags shareId model
|
, Results.view texts model.uiSettings flags shareId model
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import Api.Model.ShareVerifyResult exposing (ShareVerifyResult)
|
|||||||
import Comp.SharePasswordForm
|
import Comp.SharePasswordForm
|
||||||
import Comp.UrlCopy
|
import Comp.UrlCopy
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
|
import Data.UiSettings exposing (UiSettings)
|
||||||
import Http
|
import Http
|
||||||
|
|
||||||
|
|
||||||
@ -37,12 +38,14 @@ type alias Model =
|
|||||||
, pageError : PageError
|
, pageError : PageError
|
||||||
, attachMenuOpen : Bool
|
, attachMenuOpen : Bool
|
||||||
, visibleAttach : Int
|
, visibleAttach : Int
|
||||||
|
, uiSettings : UiSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type Msg
|
type Msg
|
||||||
= VerifyResp (Result Http.Error ShareVerifyResult)
|
= VerifyResp (Result Http.Error ShareVerifyResult)
|
||||||
| GetItemResp (Result Http.Error ItemDetail)
|
| GetItemResp (Result Http.Error ItemDetail)
|
||||||
|
| UiSettingsResp (Result Http.Error UiSettings)
|
||||||
| PasswordMsg Comp.SharePasswordForm.Msg
|
| PasswordMsg Comp.SharePasswordForm.Msg
|
||||||
| SelectActiveAttachment Int
|
| SelectActiveAttachment Int
|
||||||
| ToggleSelectAttach
|
| ToggleSelectAttach
|
||||||
@ -58,6 +61,7 @@ emptyModel vm =
|
|||||||
, pageError = PageErrorNone
|
, pageError = PageErrorNone
|
||||||
, attachMenuOpen = False
|
, attachMenuOpen = False
|
||||||
, visibleAttach = 0
|
, visibleAttach = 0
|
||||||
|
, uiSettings = Data.UiSettings.defaults
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,7 +25,10 @@ update shareId itemId flags msg model =
|
|||||||
, viewMode = ViewLoading
|
, viewMode = ViewLoading
|
||||||
, verifyResult = res
|
, 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
|
else if res.passwordRequired then
|
||||||
@ -93,3 +96,9 @@ update shareId itemId flags msg model =
|
|||||||
Comp.UrlCopy.update lm
|
Comp.UrlCopy.update lm
|
||||||
in
|
in
|
||||||
( model, cmd )
|
( model, cmd )
|
||||||
|
|
||||||
|
UiSettingsResp (Ok s) ->
|
||||||
|
( { model | uiSettings = s }, Cmd.none )
|
||||||
|
|
||||||
|
UiSettingsResp (Err _) ->
|
||||||
|
( model, Cmd.none )
|
||||||
|
@ -16,7 +16,7 @@ import Comp.UrlCopy
|
|||||||
import Data.Fields
|
import Data.Fields
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.Icons as Icons
|
import Data.Icons as Icons
|
||||||
import Data.ItemTemplate as IT exposing (ItemTemplate)
|
import Data.ItemTemplate as IT
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
@ -33,7 +33,7 @@ import Util.String
|
|||||||
|
|
||||||
|
|
||||||
viewSidebar : Texts -> Bool -> Flags -> UiSettings -> String -> String -> Model -> Html Msg
|
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
|
div
|
||||||
[ id "sidebar"
|
[ id "sidebar"
|
||||||
, classList [ ( "hidden", not visible || model.viewMode /= ViewNormal ) ]
|
, classList [ ( "hidden", not visible || model.viewMode /= ViewNormal ) ]
|
||||||
@ -103,6 +103,7 @@ itemData texts flags model shareId itemId =
|
|||||||
div
|
div
|
||||||
[ class "flex ml-2 mt-1 font-semibold hover:opacity-75"
|
[ class "flex ml-2 mt-1 font-semibold hover:opacity-75"
|
||||||
, class S.basicLabel
|
, class S.basicLabel
|
||||||
|
, class (Data.UiSettings.tagColorString2 tag model.uiSettings)
|
||||||
]
|
]
|
||||||
[ i [ class "fa fa-tag mr-2" ] []
|
[ i [ class "fa fa-tag mr-2" ] []
|
||||||
, text tag.name
|
, text tag.name
|
||||||
|
@ -42,10 +42,10 @@ type alias Model =
|
|||||||
|
|
||||||
|
|
||||||
init : Flags -> UiSettings -> ( Model, Cmd Msg )
|
init : Flags -> UiSettings -> ( Model, Cmd Msg )
|
||||||
init flags settings =
|
init flags _ =
|
||||||
let
|
let
|
||||||
( um, uc ) =
|
( um, uc ) =
|
||||||
Comp.UiSettingsManage.init flags settings
|
Comp.UiSettingsManage.init flags
|
||||||
|
|
||||||
( otpm, otpc ) =
|
( otpm, otpc ) =
|
||||||
Comp.OtpSetup.init flags
|
Comp.OtpSetup.init flags
|
||||||
@ -107,5 +107,3 @@ type Msg
|
|||||||
| NotificationHookMsg Comp.NotificationHookManage.Msg
|
| NotificationHookMsg Comp.NotificationHookManage.Msg
|
||||||
| PeriodicQueryMsg Comp.PeriodicQueryTaskManage.Msg
|
| PeriodicQueryMsg Comp.PeriodicQueryTaskManage.Msg
|
||||||
| ChannelMsg Comp.NotificationChannelManage.Msg
|
| ChannelMsg Comp.NotificationChannelManage.Msg
|
||||||
| UpdateSettings
|
|
||||||
| ReceiveBrowserSettings StoredUiSettings
|
|
||||||
|
@ -17,6 +17,7 @@ import Comp.OtpSetup
|
|||||||
import Comp.PeriodicQueryTaskManage
|
import Comp.PeriodicQueryTaskManage
|
||||||
import Comp.ScanMailboxManage
|
import Comp.ScanMailboxManage
|
||||||
import Comp.UiSettingsManage
|
import Comp.UiSettingsManage
|
||||||
|
import Data.AppEvent exposing (AppEvent(..))
|
||||||
import Data.Flags exposing (Flags)
|
import Data.Flags exposing (Flags)
|
||||||
import Data.UiSettings exposing (UiSettings)
|
import Data.UiSettings exposing (UiSettings)
|
||||||
import Page.UserSettings.Data exposing (..)
|
import Page.UserSettings.Data exposing (..)
|
||||||
@ -26,10 +27,15 @@ type alias UpdateResult =
|
|||||||
{ model : Model
|
{ model : Model
|
||||||
, cmd : Cmd Msg
|
, cmd : Cmd Msg
|
||||||
, sub : Sub 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 -> UiSettings -> Msg -> Model -> UpdateResult
|
||||||
update flags settings msg model =
|
update flags settings msg model =
|
||||||
case msg of
|
case msg of
|
||||||
@ -47,7 +53,7 @@ update flags settings msg model =
|
|||||||
{ model = { m | emailSettingsModel = em }
|
{ model = { m | emailSettingsModel = em }
|
||||||
, cmd = Cmd.map EmailSettingsMsg c
|
, cmd = Cmd.map EmailSettingsMsg c
|
||||||
, sub = Sub.none
|
, sub = Sub.none
|
||||||
, newSettings = Nothing
|
, appEvent = AppNothing
|
||||||
}
|
}
|
||||||
|
|
||||||
ImapSettingsTab ->
|
ImapSettingsTab ->
|
||||||
@ -58,18 +64,14 @@ update flags settings msg model =
|
|||||||
{ model = { m | imapSettingsModel = em }
|
{ model = { m | imapSettingsModel = em }
|
||||||
, cmd = Cmd.map ImapSettingsMsg c
|
, cmd = Cmd.map ImapSettingsMsg c
|
||||||
, sub = Sub.none
|
, sub = Sub.none
|
||||||
, newSettings = Nothing
|
, appEvent = AppNothing
|
||||||
}
|
}
|
||||||
|
|
||||||
ChangePassTab ->
|
ChangePassTab ->
|
||||||
UpdateResult m Cmd.none Sub.none Nothing
|
unit m
|
||||||
|
|
||||||
NotificationTab ->
|
NotificationTab ->
|
||||||
{ model = m
|
unit m
|
||||||
, cmd = Cmd.none
|
|
||||||
, sub = Sub.none
|
|
||||||
, newSettings = Nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
NotificationWebhookTab ->
|
NotificationWebhookTab ->
|
||||||
let
|
let
|
||||||
@ -79,7 +81,7 @@ update flags settings msg model =
|
|||||||
{ model = m
|
{ model = m
|
||||||
, cmd = Cmd.map NotificationHookMsg nc
|
, cmd = Cmd.map NotificationHookMsg nc
|
||||||
, sub = Sub.none
|
, sub = Sub.none
|
||||||
, newSettings = Nothing
|
, appEvent = AppNothing
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationQueriesTab ->
|
NotificationQueriesTab ->
|
||||||
@ -88,7 +90,7 @@ update flags settings msg model =
|
|||||||
Cmd.map NotificationMsg
|
Cmd.map NotificationMsg
|
||||||
(Tuple.second (Comp.DueItemsTaskManage.init flags))
|
(Tuple.second (Comp.DueItemsTaskManage.init flags))
|
||||||
in
|
in
|
||||||
UpdateResult m initCmd Sub.none Nothing
|
UpdateResult m initCmd Sub.none AppNothing
|
||||||
|
|
||||||
NotificationDueItemsTab ->
|
NotificationDueItemsTab ->
|
||||||
let
|
let
|
||||||
@ -96,7 +98,7 @@ update flags settings msg model =
|
|||||||
Cmd.map NotificationMsg
|
Cmd.map NotificationMsg
|
||||||
(Tuple.second (Comp.DueItemsTaskManage.init flags))
|
(Tuple.second (Comp.DueItemsTaskManage.init flags))
|
||||||
in
|
in
|
||||||
UpdateResult m initCmd Sub.none Nothing
|
UpdateResult m initCmd Sub.none AppNothing
|
||||||
|
|
||||||
ScanMailboxTab ->
|
ScanMailboxTab ->
|
||||||
let
|
let
|
||||||
@ -104,16 +106,24 @@ update flags settings msg model =
|
|||||||
Cmd.map ScanMailboxMsg
|
Cmd.map ScanMailboxMsg
|
||||||
(Tuple.second (Comp.ScanMailboxManage.init flags))
|
(Tuple.second (Comp.ScanMailboxManage.init flags))
|
||||||
in
|
in
|
||||||
UpdateResult m initCmd Sub.none Nothing
|
UpdateResult m initCmd Sub.none AppNothing
|
||||||
|
|
||||||
UiSettingsTab ->
|
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 ->
|
OtpTab ->
|
||||||
UpdateResult m Cmd.none Sub.none Nothing
|
unit m
|
||||||
|
|
||||||
ChannelTab ->
|
ChannelTab ->
|
||||||
UpdateResult m Cmd.none Sub.none Nothing
|
unit m
|
||||||
|
|
||||||
ChangePassMsg m ->
|
ChangePassMsg m ->
|
||||||
let
|
let
|
||||||
@ -123,7 +133,7 @@ update flags settings msg model =
|
|||||||
{ model = { model | changePassModel = m2 }
|
{ model = { model | changePassModel = m2 }
|
||||||
, cmd = Cmd.map ChangePassMsg c2
|
, cmd = Cmd.map ChangePassMsg c2
|
||||||
, sub = Sub.none
|
, sub = Sub.none
|
||||||
, newSettings = Nothing
|
, appEvent = AppNothing
|
||||||
}
|
}
|
||||||
|
|
||||||
EmailSettingsMsg m ->
|
EmailSettingsMsg m ->
|
||||||
@ -134,7 +144,7 @@ update flags settings msg model =
|
|||||||
{ model = { model | emailSettingsModel = m2 }
|
{ model = { model | emailSettingsModel = m2 }
|
||||||
, cmd = Cmd.map EmailSettingsMsg c2
|
, cmd = Cmd.map EmailSettingsMsg c2
|
||||||
, sub = Sub.none
|
, sub = Sub.none
|
||||||
, newSettings = Nothing
|
, appEvent = AppNothing
|
||||||
}
|
}
|
||||||
|
|
||||||
ImapSettingsMsg m ->
|
ImapSettingsMsg m ->
|
||||||
@ -145,7 +155,7 @@ update flags settings msg model =
|
|||||||
{ model = { model | imapSettingsModel = m2 }
|
{ model = { model | imapSettingsModel = m2 }
|
||||||
, cmd = Cmd.map ImapSettingsMsg c2
|
, cmd = Cmd.map ImapSettingsMsg c2
|
||||||
, sub = Sub.none
|
, sub = Sub.none
|
||||||
, newSettings = Nothing
|
, appEvent = AppNothing
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationMsg lm ->
|
NotificationMsg lm ->
|
||||||
@ -156,7 +166,7 @@ update flags settings msg model =
|
|||||||
{ model = { model | notificationModel = m2 }
|
{ model = { model | notificationModel = m2 }
|
||||||
, cmd = Cmd.map NotificationMsg c2
|
, cmd = Cmd.map NotificationMsg c2
|
||||||
, sub = Sub.none
|
, sub = Sub.none
|
||||||
, newSettings = Nothing
|
, appEvent = AppNothing
|
||||||
}
|
}
|
||||||
|
|
||||||
ScanMailboxMsg lm ->
|
ScanMailboxMsg lm ->
|
||||||
@ -167,7 +177,7 @@ update flags settings msg model =
|
|||||||
{ model = { model | scanMailboxModel = m2 }
|
{ model = { model | scanMailboxModel = m2 }
|
||||||
, cmd = Cmd.map ScanMailboxMsg c2
|
, cmd = Cmd.map ScanMailboxMsg c2
|
||||||
, sub = Sub.none
|
, sub = Sub.none
|
||||||
, newSettings = Nothing
|
, appEvent = AppNothing
|
||||||
}
|
}
|
||||||
|
|
||||||
UiSettingsMsg lm ->
|
UiSettingsMsg lm ->
|
||||||
@ -178,7 +188,7 @@ update flags settings msg model =
|
|||||||
{ model = { model | uiSettingsModel = res.model }
|
{ model = { model | uiSettingsModel = res.model }
|
||||||
, cmd = Cmd.map UiSettingsMsg res.cmd
|
, cmd = Cmd.map UiSettingsMsg res.cmd
|
||||||
, sub = Sub.map UiSettingsMsg res.sub
|
, sub = Sub.map UiSettingsMsg res.sub
|
||||||
, newSettings = res.newSettings
|
, appEvent = res.appEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
OtpSetupMsg lm ->
|
OtpSetupMsg lm ->
|
||||||
@ -189,7 +199,7 @@ update flags settings msg model =
|
|||||||
{ model = { model | otpSetupModel = otpm }
|
{ model = { model | otpSetupModel = otpm }
|
||||||
, cmd = Cmd.map OtpSetupMsg otpc
|
, cmd = Cmd.map OtpSetupMsg otpc
|
||||||
, sub = Sub.none
|
, sub = Sub.none
|
||||||
, newSettings = Nothing
|
, appEvent = AppNothing
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationHookMsg lm ->
|
NotificationHookMsg lm ->
|
||||||
@ -200,7 +210,7 @@ update flags settings msg model =
|
|||||||
{ model = { model | notificationHookModel = hm }
|
{ model = { model | notificationHookModel = hm }
|
||||||
, cmd = Cmd.map NotificationHookMsg hc
|
, cmd = Cmd.map NotificationHookMsg hc
|
||||||
, sub = Sub.none
|
, sub = Sub.none
|
||||||
, newSettings = Nothing
|
, appEvent = AppNothing
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelMsg lm ->
|
ChannelMsg lm ->
|
||||||
@ -211,22 +221,9 @@ update flags settings msg model =
|
|||||||
{ model = { model | channelModel = cm }
|
{ model = { model | channelModel = cm }
|
||||||
, cmd = Cmd.map ChannelMsg cc
|
, cmd = Cmd.map ChannelMsg cc
|
||||||
, sub = Sub.none
|
, 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 ->
|
PeriodicQueryMsg lm ->
|
||||||
let
|
let
|
||||||
( pqm, pqc, pqs ) =
|
( pqm, pqc, pqs ) =
|
||||||
@ -235,5 +232,5 @@ update flags settings msg model =
|
|||||||
{ model = { model | periodicQueryModel = pqm }
|
{ model = { model | periodicQueryModel = pqm }
|
||||||
, cmd = Cmd.map PeriodicQueryMsg pqc
|
, cmd = Cmd.map PeriodicQueryMsg pqc
|
||||||
, sub = Sub.map PeriodicQueryMsg pqs
|
, sub = Sub.map PeriodicQueryMsg pqs
|
||||||
, newSettings = Nothing
|
, appEvent = AppNothing
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,7 @@ port module Ports exposing
|
|||||||
, printElement
|
, printElement
|
||||||
, receiveCheckQueryResult
|
, receiveCheckQueryResult
|
||||||
, receiveServerEvent
|
, receiveServerEvent
|
||||||
, receiveUiSettings
|
|
||||||
, removeAccount
|
, removeAccount
|
||||||
, requestUiSettings
|
|
||||||
, setAccount
|
, setAccount
|
||||||
, setUiTheme
|
, setUiTheme
|
||||||
)
|
)
|
||||||
@ -21,7 +19,6 @@ port module Ports exposing
|
|||||||
import Api.Model.AuthResult exposing (AuthResult)
|
import Api.Model.AuthResult exposing (AuthResult)
|
||||||
import Data.QueryParseResult exposing (QueryParseResult)
|
import Data.QueryParseResult exposing (QueryParseResult)
|
||||||
import Data.ServerEvent exposing (ServerEvent)
|
import Data.ServerEvent exposing (ServerEvent)
|
||||||
import Data.UiSettings exposing (StoredUiSettings)
|
|
||||||
import Data.UiTheme exposing (UiTheme)
|
import Data.UiTheme exposing (UiTheme)
|
||||||
import Json.Decode as D
|
import Json.Decode as D
|
||||||
|
|
||||||
@ -46,12 +43,6 @@ port receiveCheckQueryResult : (QueryParseResult -> msg) -> Sub msg
|
|||||||
port initClipboard : ( String, String ) -> Cmd 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
|
{-| Creates a new window/tab, writes the contents of the given element
|
||||||
and calls the print dialog.
|
and calls the print dialog.
|
||||||
-}
|
-}
|
||||||
|
@ -55,25 +55,6 @@ elmApp.ports.removeAccount.subscribe(function() {
|
|||||||
closeWS();
|
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 = {};
|
var docspell_clipboards = {};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user