From a3569598d8c996e5108bdd82021fd44927ecb452 Mon Sep 17 00:00:00 2001 From: eikek Date: Wed, 2 Mar 2022 18:33:25 +0100 Subject: [PATCH] Minor improvements - use a one-line log format - load templates only once - fixed typos - fix reset timezone in ui settings - hide bookmarks headline if there are none --- .../logging/impl/ScribeConfigure.scala | 2 +- .../docspell/restserver/RestServer.scala | 2 +- .../restserver/webapp/TemplateRoutes.scala | 63 ++---------------- .../restserver/webapp/Templates.scala | 64 +++++++++++++++++++ .../main/elm/Comp/NotificationHookForm.elm | 2 +- .../src/main/elm/Comp/UiSettingsForm.elm | 2 +- .../src/main/elm/Page/Dashboard/SideMenu.elm | 30 +++++---- 7 files changed, 91 insertions(+), 74 deletions(-) create mode 100644 modules/restserver/src/main/scala/docspell/restserver/webapp/Templates.scala diff --git a/modules/logging/scribe/src/main/scala/docspell/logging/impl/ScribeConfigure.scala b/modules/logging/scribe/src/main/scala/docspell/logging/impl/ScribeConfigure.scala index ace08881..b151f384 100644 --- a/modules/logging/scribe/src/main/scala/docspell/logging/impl/ScribeConfigure.scala +++ b/modules/logging/scribe/src/main/scala/docspell/logging/impl/ScribeConfigure.scala @@ -47,7 +47,7 @@ object ScribeConfigure { if (logger.id == scribe.Logger.RootId) { cfg.format match { case Format.Fancy => - l.withHandler(formatter = Formatter.default, writer = SystemOutWriter) + l.withHandler(formatter = Formatter.enhanced, writer = SystemOutWriter) case Format.Plain => l.withHandler(formatter = Formatter.classic, writer = SystemOutWriter) case Format.Json => diff --git a/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala b/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala index 2d85a4f9..891d177e 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala @@ -104,7 +104,7 @@ object RestServer { )( wsB: WebSocketBuilder2[F] ) = { - val templates = TemplateRoutes[F](cfg) + val templates = TemplateRoutes[F](cfg, Templates[F]) val httpApp = Router( "/internal" -> InternalHeader(internSettings.internalRouteKey) { internalRoutes(pubSub) diff --git a/modules/restserver/src/main/scala/docspell/restserver/webapp/TemplateRoutes.scala b/modules/restserver/src/main/scala/docspell/restserver/webapp/TemplateRoutes.scala index d3b24f28..ba59efb6 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/webapp/TemplateRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/webapp/TemplateRoutes.scala @@ -6,12 +6,8 @@ package docspell.restserver.webapp -import java.net.URL -import java.util.concurrent.atomic.AtomicReference - import cats.effect._ import cats.implicits._ -import fs2.text import docspell.restserver.{BuildInfo, Config} @@ -36,22 +32,16 @@ object TemplateRoutes { def serviceWorker: HttpRoutes[F] } - def apply[F[_]: Async](cfg: Config): InnerRoutes[F] = { - val indexTemplate = memo( - loadResource("/index.html").flatMap(loadTemplate(_)) - ) - val docTemplate = memo(loadResource("/doc.html").flatMap(loadTemplate(_))) - val swTemplate = memo(loadResource("/sw.js").flatMap(loadTemplate(_))) - + def apply[F[_]: Async](cfg: Config, templates: Templates[F]): InnerRoutes[F] = { val dsl = new Http4sDsl[F] {} import dsl._ new InnerRoutes[F] { def doc = HttpRoutes.of[F] { case GET -> Root => for { - templ <- docTemplate + docTemplate <- templates.doc resp <- Ok( - DocData().render(templ), + DocData().render(docTemplate), `Content-Type`(textHtml, Charset.`UTF-8`) ) } yield resp @@ -59,9 +49,9 @@ object TemplateRoutes { def app = HttpRoutes.of[F] { case GET -> _ => for { - templ <- indexTemplate + indexTemplate <- templates.index resp <- Ok( - IndexData(cfg).render(templ), + IndexData(cfg).render(indexTemplate), `Content-Type`(textHtml, Charset.`UTF-8`) ) } yield resp @@ -70,9 +60,9 @@ object TemplateRoutes { def serviceWorker = HttpRoutes.of[F] { case GET -> _ => for { - templ <- swTemplate + swTemplate <- templates.serviceWorker resp <- Ok( - IndexData(cfg).render(templ), + IndexData(cfg).render(swTemplate), `Content-Type`(appJavascript, Charset.`UTF-8`) ) } yield resp @@ -80,31 +70,6 @@ object TemplateRoutes { } } - def loadResource[F[_]: Sync](name: String): F[URL] = - Option(getClass.getResource(name)) match { - case None => - Sync[F].raiseError(new Exception("Unknown resource: " + name)) - case Some(r) => - r.pure[F] - } - - def loadUrl[F[_]: Sync](url: URL): F[String] = - fs2.io - .readInputStream(Sync[F].delay(url.openStream()), 64 * 1024) - .through(text.utf8.decode) - .compile - .string - - def parseTemplate[F[_]: Sync](str: String): F[Template] = - Sync[F].pure(mustache.parse(str).leftMap(err => new Exception(err._2))).rethrow - - def loadTemplate[F[_]: Sync](url: URL): F[Template] = { - val logger = docspell.logging.getLogger[F] - loadUrl[F](url).flatMap(parseTemplate[F]).flatMap { t => - logger.info(s"Compiled template $url") *> t.pure[F] - } - } - case class DocData(swaggerRoot: String, openapiSpec: String) object DocData { @@ -150,18 +115,4 @@ object TemplateRoutes { implicit def yamuscaValueConverter: ValueConverter[IndexData] = deriveValueConverter[IndexData] } - - private def memo[F[_]: Sync, A](fa: => F[A]): F[A] = { - val ref = new AtomicReference[A]() - Sync[F].defer { - Option(ref.get) match { - case Some(a) => a.pure[F] - case None => - fa.map { a => - ref.set(a) - a - } - } - } - } } diff --git a/modules/restserver/src/main/scala/docspell/restserver/webapp/Templates.scala b/modules/restserver/src/main/scala/docspell/restserver/webapp/Templates.scala new file mode 100644 index 00000000..2f0ba336 --- /dev/null +++ b/modules/restserver/src/main/scala/docspell/restserver/webapp/Templates.scala @@ -0,0 +1,64 @@ +/* + * Copyright 2020 Eike K. & Contributors + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package docspell.restserver.webapp + +import java.net.URL + +import cats.effect._ +import cats.effect.unsafe.implicits._ +import cats.implicits._ +import fs2.text + +import yamusca.imports._ + +trait Templates[F[_]] { + def index: F[Template] + def doc: F[Template] + def serviceWorker: F[Template] +} + +object Templates { + + def apply[F[_]: Sync]: Templates[F] = + new Templates[F] { + def index = Templates.indexTemplate.pure[F] + def doc = Templates.docTemplate.pure[F] + def serviceWorker = Templates.swTemplate.pure[F] + } + + private lazy val indexTemplate = fromResource[IO]("/index.html").unsafeRunSync() + private lazy val docTemplate = fromResource[IO]("/doc.html").unsafeRunSync() + private lazy val swTemplate = fromResource[IO]("/sw.js").unsafeRunSync() + + def fromResource[F[_]: Sync](path: String): F[Template] = + loadResource[F](path).flatMap(loadTemplate[F](_)) + + private def loadResource[F[_]: Sync](name: String): F[URL] = + Option(getClass.getResource(name)) match { + case None => + Sync[F].raiseError(new Exception("Unknown resource: " + name)) + case Some(r) => + r.pure[F] + } + + private def loadUrl[F[_]: Sync](url: URL): F[String] = + fs2.io + .readInputStream(Sync[F].delay(url.openStream()), 64 * 1024) + .through(text.utf8.decode) + .compile + .string + + private def parseTemplate[F[_]: Sync](str: String): F[Template] = + Sync[F].pure(mustache.parse(str).leftMap(err => new Exception(err._2))).rethrow + + private def loadTemplate[F[_]: Sync](url: URL): F[Template] = { + val logger = docspell.logging.getLogger[F] + loadUrl[F](url).flatMap(parseTemplate[F]).flatMap { t => + logger.info(s"Compiled template $url") *> t.pure[F] + } + } +} diff --git a/modules/webapp/src/main/elm/Comp/NotificationHookForm.elm b/modules/webapp/src/main/elm/Comp/NotificationHookForm.elm index bfaa19ae..4835f7ef 100644 --- a/modules/webapp/src/main/elm/Comp/NotificationHookForm.elm +++ b/modules/webapp/src/main/elm/Comp/NotificationHookForm.elm @@ -308,7 +308,7 @@ view texts settings model = (Comp.EventSample.viewJson texts.eventSample False model.eventSampleModel) ] , div [ class "mt-4" ] - [ formHeader "Test Delviery" + [ formHeader "Test Delivery" , Html.map DeliveryTestMsg (Comp.NotificationTest.view { runDisabled = getHook model == Nothing } diff --git a/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm b/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm index 2c8106c6..aecd189f 100644 --- a/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm +++ b/modules/webapp/src/main/elm/Comp/UiSettingsForm.elm @@ -569,7 +569,7 @@ update flags sett msg model = newSettings = case tab of GeneralTab -> - { sett | uiLang = Nothing, sideMenuVisible = Nothing } + { sett | uiLang = Nothing, timeZone = Nothing, sideMenuVisible = Nothing } SearchTab -> { sett diff --git a/modules/webapp/src/main/elm/Page/Dashboard/SideMenu.elm b/modules/webapp/src/main/elm/Page/Dashboard/SideMenu.elm index b6c1d4b1..4415636a 100644 --- a/modules/webapp/src/main/elm/Page/Dashboard/SideMenu.elm +++ b/modules/webapp/src/main/elm/Page/Dashboard/SideMenu.elm @@ -31,20 +31,22 @@ view texts versionInfo _ model = , menuLink [ Page.href (SearchPage Nothing) ] (Icons.searchIcon "") texts.basics.items , menuLink [ onClick InitUpload, href "#" ] (Icons.fileUploadIcon "") texts.uploadFiles ] - , h3 - [ class S.header3 - , class "italic mt-3" - ] - [ text texts.bookmarks - ] - , div [ class "ml-2" ] - [ Html.map BookmarkMsg - (Comp.BookmarkChooser.viewWith - { showUser = True, showCollective = True, showShares = False } - texts.bookmarkChooser - model.sideMenu.bookmarkChooser - Comp.BookmarkChooser.emptySelection - ) + , div [ classList [ ( "hidden", Comp.BookmarkChooser.isEmpty model.sideMenu.bookmarkChooser ) ] ] + [ h3 + [ class S.header3 + , class "italic mt-3" + ] + [ text texts.bookmarks + ] + , div [ class "ml-2" ] + [ Html.map BookmarkMsg + (Comp.BookmarkChooser.viewWith + { showUser = True, showCollective = True, showShares = False } + texts.bookmarkChooser + model.sideMenu.bookmarkChooser + Comp.BookmarkChooser.emptySelection + ) + ] ] , h3 [ class S.header3