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