diff --git a/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala b/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala index 83005fea..6d856522 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/RestServer.scala @@ -38,6 +38,7 @@ object RestServer { "/api/doc" -> templates.doc, "/app/assets" -> WebjarRoutes.appRoutes[F](pools.blocker), "/app" -> templates.app, + "/sw.js" -> templates.serviceWorker, "/" -> redirectTo("/app") ).orNotFound 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 8c4962e5..bef2f76c 100644 --- a/modules/restserver/src/main/scala/docspell/restserver/webapp/TemplateRoutes.scala +++ b/modules/restserver/src/main/scala/docspell/restserver/webapp/TemplateRoutes.scala @@ -21,11 +21,13 @@ import yamusca.imports._ object TemplateRoutes { private[this] val logger = getLogger - val `text/html` = new MediaType("text", "html") + val `text/html` = new MediaType("text", "html") + val `application/javascript` = new MediaType("application", "javascript") trait InnerRoutes[F[_]] { def doc: HttpRoutes[F] def app: HttpRoutes[F] + def serviceWorker: HttpRoutes[F] } def apply[F[_]: Effect](blocker: Blocker, cfg: Config)(implicit @@ -35,6 +37,7 @@ object TemplateRoutes { loadResource("/index.html").flatMap(loadTemplate(_, blocker)) ) val docTemplate = memo(loadResource("/doc.html").flatMap(loadTemplate(_, blocker))) + val swTemplate = memo(loadResource("/sw.js").flatMap(loadTemplate(_, blocker))) val dsl = new Http4sDsl[F] {} import dsl._ @@ -43,14 +46,31 @@ object TemplateRoutes { HttpRoutes.of[F] { case GET -> Root => for { templ <- docTemplate - resp <- Ok(DocData().render(templ), `Content-Type`(`text/html`)) + resp <- Ok( + DocData().render(templ), + `Content-Type`(`text/html`, Charset.`UTF-8`) + ) } yield resp } def app = HttpRoutes.of[F] { case GET -> _ => for { templ <- indexTemplate - resp <- Ok(IndexData(cfg).render(templ), `Content-Type`(`text/html`)) + resp <- Ok( + IndexData(cfg).render(templ), + `Content-Type`(`text/html`, Charset.`UTF-8`) + ) + } yield resp + } + + def serviceWorker = + HttpRoutes.of[F] { case GET -> _ => + for { + templ <- swTemplate + resp <- Ok( + IndexData(cfg).render(templ), + `Content-Type`(`application/javascript`, Charset.`UTF-8`) + ) } yield resp } } diff --git a/modules/restserver/src/main/templates/index.html b/modules/restserver/src/main/templates/index.html index 2270acef..bdb1cd42 100644 --- a/modules/restserver/src/main/templates/index.html +++ b/modules/restserver/src/main/templates/index.html @@ -46,6 +46,13 @@ }; + diff --git a/modules/restserver/src/main/templates/sw.js b/modules/restserver/src/main/templates/sw.js new file mode 100644 index 00000000..243e61c5 --- /dev/null +++ b/modules/restserver/src/main/templates/sw.js @@ -0,0 +1,37 @@ +// use a cacheName for cache versioning +var cacheName = 'v1:static'; + +// during the install phase you usually want to cache static assets +self.addEventListener('install', function(e) { + // once the SW is installed, go ahead and fetch the resources to make this work offline + e.waitUntil( + caches.open(cacheName).then(function(cache) { + return cache.addAll([ + {{# cssUrls }} + '{{.}}', + {{/ cssUrls }} + {{# jsUrls }} + '{{.}}', + {{/ jsUrls}} + '{{appExtraJs}}' + ]).then(function() { + self.skipWaiting(); + }); + }) + ); +}); + +// when the browser fetches a url +self.addEventListener('fetch', function(event) { + // either respond with the cached object or go ahead and fetch the actual url + event.respondWith( + caches.match(event.request).then(function(response) { + if (response) { + // retrieve from cache + return response; + } + // fetch as normal + return fetch(event.request); + }) + ); +}); diff --git a/modules/webapp/src/main/elm/Page/Home/View.elm b/modules/webapp/src/main/elm/Page/Home/View.elm index 14287797..69f0b2d4 100644 --- a/modules/webapp/src/main/elm/Page/Home/View.elm +++ b/modules/webapp/src/main/elm/Page/Home/View.elm @@ -87,6 +87,7 @@ view current flags settings model = , div [ classList [ ( "sixteen wide column", True ) + , ( "hidden invisible", resultsBelowLimit settings model ) ] ] [ div [ class "ui basic center aligned segment" ] @@ -94,7 +95,6 @@ view current flags settings model = [ classList [ ( "ui basic tiny button", True ) , ( "disabled", not model.moreAvailable ) - , ( "hidden invisible", resultsBelowLimit settings model ) ] , disabled (not model.moreAvailable || model.moreInProgress || model.searchInProgress) , title "Load more items" diff --git a/modules/webapp/src/main/webjar/docspell.css b/modules/webapp/src/main/webjar/docspell.css index 0902853e..98537901 100644 --- a/modules/webapp/src/main/webjar/docspell.css +++ b/modules/webapp/src/main/webjar/docspell.css @@ -37,7 +37,7 @@ } .default-layout { background: #fff; -/* height: 100vh; */ + height: 100%; } .default-layout .main-content { @@ -253,7 +253,10 @@ label span.muted { .login-layout, .register-layout, .newinvite-layout { background: #708090; - height: 101vh; + height: 100%; +} +.login-layout > .ui.footer, .register-layout > .ui.footer, .newinvite-layout > .ui.footer { + background: #708090; } .login-layout .login-view, .register-layout .register-view, .newinvite-view {