Add Progressive Web App examples and docs user/dbs/progressive_web_app_example
authorDan Scott <dscott@laurentian.ca>
Fri, 14 Apr 2017 04:29:04 +0000 (00:29 -0400)
committerDan Scott <dscott@laurentian.ca>
Fri, 14 Apr 2017 04:29:04 +0000 (00:29 -0400)
Reflect the state of the Evergreen PWA as presented at the
2017 Evergreen International Conference.

Signed-off-by: Dan Scott <dscott@laurentian.ca>
Open-ILS/examples/pwa/README.adoc [new file with mode: 0644]
Open-ILS/examples/pwa/sw-precache.js [new file with mode: 0644]
Open-ILS/examples/pwa/templates/opac/parts/sw-register.tt2 [new file with mode: 0644]
Open-ILS/examples/pwa/web/images/icon192.png [new file with mode: 0644]
Open-ILS/examples/pwa/web/images/icon48.png [new file with mode: 0644]
Open-ILS/examples/pwa/web/images/icon96.png [new file with mode: 0644]
Open-ILS/examples/pwa/web/manifest.json [new file with mode: 0644]

diff --git a/Open-ILS/examples/pwa/README.adoc b/Open-ILS/examples/pwa/README.adoc
new file mode 100644 (file)
index 0000000..8bc0e23
--- /dev/null
@@ -0,0 +1,128 @@
+= Progressive Web App (PWA) =
+
+This document and associated samples will help reproduce the state of the PWA
+demonstrated at the Evergreen International Conference 2017 by Dan Scott during
+his talk https://stuff.coffeecode.net/2017/evergreen-progressive-web-app[We aim
+to misbehave - Evergreen: Progressive Web App].
+
+For a very basic PWA, we need to offer:
+
+. a manifest to give browsers the URL, icons, descriptions, and theme they need
+  to launch in a fashion similar to native apps
+. a service worker that can handle poor or entirely offline network conditions
+  by intercepting network requests and rerouting them to a dedicated local
+  cache (if necessary). By bypassing the network, we can also reduce the overall
+  bandwidth consumed and improve the performance of the application.
+
+We will focus on the public catalogue at /eg/opac and specifically the My Account
+section at /eg/opac/myopac. However, the service worker must be registered at
+the highest scope to exert control over its assets, and the existence of a
+number of relevant CSS and JavaScript files under paths like /js and /css means
+it will have to be registered at the root (/).
+
+== Create the service worker ==
+
+. In the `service-worker` directory, run `npm install` to install the
+  `sw-precache` package.
+. Adjust `sw-precache.js` to reflect your site's logo files, etc.
+. Run `node_modules/sw-precache/cli.js --config sw-precache.js` to generate
+  the `service-worker.js` file, based on the configuration in `sw-precache.js`
+. Copy `service-worker.js` into the `web` directory.
+. Copy the service worker registration template into the public catalogue
+  templates directory:
++
+[source,bash]
+------------------------------------------------------------
+cp templates/opac/parts/sw-register.tt2 /openils/var/templates/opac/parts/.
+------------------------------------------------------------
++
+. Add the service worker registration code to the `<head>` section of
+  `templates/opac/parts/base.tt2`:
++
+[source,txt]
+------------------------------------------------------------
+[% INCLUDE 'opac/parts/sw-register.tt2' %]
+------------------------------------------------------------
+
+== Add a manifest ==
+
+. Add the following lines to `templates/opac/parts/base.tt2`:
++
+[source,html]
+------------------------------------------------------------
+<link rel="manifest" href="/manifest.json">
+<link rel="icon" sizes="192x192" href="/images/icon192.png">
+<meta name="theme-color" content="#007a54">
+------------------------------------------------------------
++
+. Adjust the manifest details to suit your theme and site name:
+.. Keep the `name` and `short_name` fields to 12 characters or
+   less, and keep them the same.
+.. Also match the `theme-color` `<meta>` value with the `theme_color` field in
+   the manifest.
+
+== Deploy the code! ==
+
+. Copy the contents of the `web` directory into `/openils/var/web`:
++
+[source,bash]
+------------------------------------------------------------
+cp -r web/* /openils/var/web/.
+------------------------------------------------------------
+
+== Optimizations ==
+
+If you are running the staff client with requests proxied by nginx, you can
+take advantage of the support that has been built into modern versions of
+nginx for the https://http2.github.io/[HTTP/2] protocol and gain the advantages
+of multiplexing by simply adding 'http2' to your `listen` line:
+
+[source,txt]
+------------------------------------------------------------
+server {
+    listen 443 ssl http2;
+------------------------------------------------------------
+
+We're loading Dojo on every page, even though by default the only page that
+absolutely requires it is the advanced search page. This results in
+approximately an extra half-second initial page load time, which makes
+https://developers.google.com/web/tools/lighthouse/[Lighthouse] unhappy about
+our performance. The https://bugs.launchpad.net/evergreen/+bug/1411699[Don't
+load Dojo widgets] branch shifts the requirement to only the advanced search
+page.
+
+Our "My Account" pages are currently hard-coded as `Expires: -1` and
+`Cache-Control: no-store` which makes browsers reluctant to cache them,
+understandably. The behaviour of the `sw-precache` generator appears to be to
+respect the header and not cache the request. Commit e7f11d5 in this branch
+removes the hard-coded setting from `EGCatLoader.pm` and enables us to control
+this in the HTTP server configuration instead.
+
+See https://bugs.launchpad.net/evergreen/+bug/1681095[Extend browser
+cache-busting branch] for a way to greatly extend the cache expiration for all
+non-HTML assets while still allowing changes to propagate quickly, if
+necessary.
+
+== Limitations ==
+
+There are currently many!
+
+As the public catalogue currently appends stateful GET params to URLs, even when
+accessing `My Account`, the cached content can often only be accessed if you
+follow the same path to access a given page when offline. For example, if you
+start a session by searching for "Potter", and then click on "My Account", any
+of the account pages you visit will have a `;query=Potter` param attached to
+their URL. If you happen to perform a different search, then access "My Account",
+you may not be able to access the page at all, or you might see different cached
+results.
+
+Searching offline doesn't make much sense anyway. Ideally we would detect when
+network conditions are bad and serve up an offline page with options greyed out
+if they are unlikely to work, with highlighting of options that are available
+offline.
+
+== Icon credits ==
+
+The icons in `web/images` are based on the PWA logo made freely available by
+Chris Love at https://github.com/docluv/pwa-logo under the terms _The logo
+should be considered publically available for everyone to use._
diff --git a/Open-ILS/examples/pwa/sw-precache.js b/Open-ILS/examples/pwa/sw-precache.js
new file mode 100644 (file)
index 0000000..dff6001
--- /dev/null
@@ -0,0 +1,47 @@
+module.exports = {
+  staticFileGlobs: [
+    '/openils/var/web/favicon.ico',
+    '/openils/var/web/offline.html',
+    '/openils/var/web/js/sw-register.js',
+    '/openils/var/web/css/skin/default/opac/semiauto.css',
+    '/openils/var/web/js/ui/default/opac/simple.js',
+    '/openils/var/web/opac/images/small_logo.png',
+    '/openils/var/web/opac/images/progressbar_green.png',
+    '/openils/var/web/opac/images/main_logo.png',
+    '/openils/var/web/opac/images/eg_tiny_logo.png'
+  ],
+  stripPrefix: '/openils/var/web/',
+  runtimeCaching: [
+    // aggressively cache images
+    {
+      urlPattern: /\.(gif|jpg|jpeg|png)$/,
+      handler: 'fastest'
+    },
+    // aggressively cache stylesheets and JavaScript
+    {
+      urlPattern: /\.(css|js)$/,
+      handler: 'fastest'
+    },
+    // cache added content too
+    {
+      urlPattern: /\/opac\/extras\/ac\//,
+      handler: 'fastest'
+    },
+    // results can take a long time to respond
+    {
+      urlPattern: /\/eg\/opac\/results\//,
+      handler: 'networkFirst',
+      options: {
+        networkTimeoutSeconds: 60
+      }
+    },
+    // prefer the network, but fall back to the cache
+    {
+      urlPattern: /\/(eg|js|opac|staff)\//,
+      handler: 'networkFirst',
+      options: {
+        networkTimeoutSeconds: 4
+      }
+    }
+  ]
+};
diff --git a/Open-ILS/examples/pwa/templates/opac/parts/sw-register.tt2 b/Open-ILS/examples/pwa/templates/opac/parts/sw-register.tt2
new file mode 100644 (file)
index 0000000..c9681ab
--- /dev/null
@@ -0,0 +1,61 @@
+<script>
+/**
+ * Copyright 2015 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// Source: https://github.com/GoogleChrome/sw-precache/blob/master/demo/app/js/service-worker-registration.js
+/* eslint-env browser */
+'use strict';
+if ('serviceWorker' in navigator) {
+  // Delay registration until after the page has loaded, to ensure that our
+  // precaching requests don't degrade the first visit experience.
+  // See https://developers.google.com/web/fundamentals/instant-and-offline/service-worker/registration
+  window.addEventListener('load', function() {
+    // Your service-worker.js *must* be located at the top-level directory relative to your site.
+    // It won't be able to control pages unless it's located at the same level or higher than them.
+    // *Don't* register service worker file in, e.g., a scripts/ sub-directory!
+    // See https://github.com/slightlyoff/ServiceWorker/issues/468
+    navigator.serviceWorker.register('/sw.js').then(function(reg) {
+      // updatefound is fired if service-worker.js changes.
+      reg.onupdatefound = function() {
+        // The updatefound event implies that reg.installing is set; see
+        // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event
+        var installingWorker = reg.installing;
+        installingWorker.onstatechange = function() {
+          switch (installingWorker.state) {
+            case 'installed':
+              if (navigator.serviceWorker.controller) {
+                // At this point, the old content will have been purged and the fresh content will
+                // have been added to the cache.
+                // It's the perfect time to display a "New content is available; please refresh."
+                // message in the page's interface.
+                console.log('New or updated content is available.');
+              } else {
+                // At this point, everything has been precached.
+                // It's the perfect time to display a "Content is cached for offline use." message.
+                console.log('Content is now available offline!');
+              }
+              break;
+            case 'redundant':
+              console.error('The installing service worker became redundant.');
+              break;
+          }
+        };
+      };
+    }).catch(function(e) {
+      console.error('Error during service worker registration:', e);
+    });
+  });
+}
+</script>
diff --git a/Open-ILS/examples/pwa/web/images/icon192.png b/Open-ILS/examples/pwa/web/images/icon192.png
new file mode 100644 (file)
index 0000000..b978eb0
Binary files /dev/null and b/Open-ILS/examples/pwa/web/images/icon192.png differ
diff --git a/Open-ILS/examples/pwa/web/images/icon48.png b/Open-ILS/examples/pwa/web/images/icon48.png
new file mode 100644 (file)
index 0000000..cae1525
Binary files /dev/null and b/Open-ILS/examples/pwa/web/images/icon48.png differ
diff --git a/Open-ILS/examples/pwa/web/images/icon96.png b/Open-ILS/examples/pwa/web/images/icon96.png
new file mode 100644 (file)
index 0000000..d7d101d
Binary files /dev/null and b/Open-ILS/examples/pwa/web/images/icon96.png differ
diff --git a/Open-ILS/examples/pwa/web/manifest.json b/Open-ILS/examples/pwa/web/manifest.json
new file mode 100644 (file)
index 0000000..364d51e
--- /dev/null
@@ -0,0 +1,27 @@
+
+  "short_name": "Shiny Cap'n",
+  "name": "Shiny Cap'n",
+  "description": "A shiny Evergreen progressive web app (PWA)",
+  "background_color": "#007a54",
+  "theme_color": "#007a54",
+  "icons": [
+    {
+      "src": "/images/icon48.png",
+      "type": "image/png",
+      "sizes": "48x48"
+    },
+    {
+      "src": "/images/icon96.png",
+      "type": "image/png",
+      "sizes": "96x96"
+    },
+    {
+      "src": "/images/icon192.png",
+      "type": "image/png",
+      "sizes": "192x192"
+    }
+  ],
+  "start_url": "/eg/opac/home",
+  "display": "standalone",
+  "orientation": "portrait"
+}