diff options
Diffstat (limited to 'templates/web/base')
-rw-r--r-- | templates/web/base/common_header_tags.html | 9 | ||||
-rw-r--r-- | templates/web/base/common_scripts.html | 1 | ||||
-rw-r--r-- | templates/web/base/header.html | 5 | ||||
-rw-r--r-- | templates/web/base/offline/fallback.html (renamed from templates/web/base/offline/appcache.html) | 8 | ||||
-rw-r--r-- | templates/web/base/offline/manifest.html | 16 | ||||
-rw-r--r-- | templates/web/base/offline/service_worker.html | 104 |
6 files changed, 118 insertions, 25 deletions
diff --git a/templates/web/base/common_header_tags.html b/templates/web/base/common_header_tags.html index 728b81363..279f561df 100644 --- a/templates/web/base/common_header_tags.html +++ b/templates/web/base/common_header_tags.html @@ -1,6 +1,7 @@ [% SET start = c.config.ADMIN_BASE_URL IF admin %] <meta http-equiv="content-type" content="text/html; charset=utf-8"> +<link rel="manifest" href="/.well-known/manifest.webmanifest"> [% IF csrf_token %] <meta content="[% csrf_token %]" name="csrf-token" /> @@ -15,6 +16,14 @@ (function(a){a=a.documentElement;a.className=a.className.replace(/\bno-js\b/,"js");var b=-1<a.className.indexOf("ie8");b=Modernizr.mq("(min-width: 48em)")||b?"desktop":"mobile";"IntersectionObserver"in window&&(a.className+=" lazyload");"mobile"==b&&(a.className+=' mobile[% " map-fullscreen only-map map-reporting" IF page == "around" %]')})(document); </script> +<script nonce="[% csp_nonce %]"> +if ('serviceWorker' in navigator) { + window.addEventListener('load', function() { + navigator.serviceWorker.register('/service-worker.js'); + }); +} +</script> + [% IF robots %] <meta name="robots" content="[% robots %]"> [% ELSIF c.config.STAGING_SITE %] diff --git a/templates/web/base/common_scripts.html b/templates/web/base/common_scripts.html index 7df8feaa0..f94b3d464 100644 --- a/templates/web/base/common_scripts.html +++ b/templates/web/base/common_scripts.html @@ -46,6 +46,7 @@ IF c.user_exists AND (c.user.from_body OR c.user.is_superuser); END; IF c.user.has_body_permission_to('planned_reports'); scripts.push( + 'https://cdn.jsdelivr.net/npm/idb-keyval@3/dist/idb-keyval-iife.min.js', version('/cobrands/fixmystreet/offline.js'), ); END; diff --git a/templates/web/base/header.html b/templates/web/base/header.html index 74ec2d16a..b3d77b3a6 100644 --- a/templates/web/base/header.html +++ b/templates/web/base/header.html @@ -6,10 +6,7 @@ <!doctype html> <!--[if IE 8]> <html class="no-js ie8"[% html_att | safe %]><![endif]--> <!--[if IE 9]> <html class="no-js ie9"[% html_att | safe %]><![endif]--> -<!--[if gt IE 9]><!--><html class="no-js"[% html_att | safe %] -[% IF appcache ~%] - manifest="/offline/appcache.manifest" -[%~ END %]><!--<![endif]--> +<!--[if gt IE 9]><!--><html class="no-js"[% html_att | safe %]><!--<![endif]--> <head> <meta name="viewport" content="initial-scale=1.0"> diff --git a/templates/web/base/offline/appcache.html b/templates/web/base/offline/fallback.html index ed48b7a00..b8e1ee9b9 100644 --- a/templates/web/base/offline/appcache.html +++ b/templates/web/base/offline/fallback.html @@ -1,11 +1,9 @@ [% SET bodyclass = "fullwidthpage offlinepage" ~%] -[% INCLUDE 'header.html' appcache = 1 %] +[% INCLUDE 'header.html' %] -<h1>[% loc('Internet glitch') %]</h1> +<h1>[% loc('Offline') %]</h1> -<p>[% loc('Sorry, we don’t have a good enough connection to fetch that page, or the -page wasn’t found or there was a server error. Please try again later.') %] -</p> +<p>[% loc('Sorry, we don’t have a good enough connection to fetch that page.') %]</p> <ul class="item-list item-list--reports" id="offline_list"></ul> diff --git a/templates/web/base/offline/manifest.html b/templates/web/base/offline/manifest.html deleted file mode 100644 index 93d26cb94..000000000 --- a/templates/web/base/offline/manifest.html +++ /dev/null @@ -1,16 +0,0 @@ -CACHE MANIFEST - -[% PROCESS 'common_scripts.html' ~%] - -CACHE: -[% version('/cobrands/' _ c.cobrand.asset_moniker _ '/base.css') %] -[% version('/cobrands/' _ c.cobrand.asset_moniker _ '/layout.css') %] -[% FOR script IN scripts ~%] - [%- script %] -[% END %] - -NETWORK: -* - -FALLBACK: -/ [% version('../templates/web/base/offline/appcache.html', '/offline/appcache') %] diff --git a/templates/web/base/offline/service_worker.html b/templates/web/base/offline/service_worker.html new file mode 100644 index 000000000..0feb26ce6 --- /dev/null +++ b/templates/web/base/offline/service_worker.html @@ -0,0 +1,104 @@ +[% +SET bodyclass = "offlinepage"; # For selection of scripts +PROCESS 'common_scripts.html'; +SET offline_html = version('../templates/web/base/offline/fallback.html', '/offline/fallback'); +SET scripts_seen = {}; + +~%] + +importScripts('https://cdn.jsdelivr.net/npm/idb-keyval@3/dist/idb-keyval-iife.min.js'); + +const requiredOffline = [ + "[% version('/cobrands/' _ c.cobrand.asset_moniker _ '/base.css') %]", + "[% version('/cobrands/' _ c.cobrand.asset_moniker _ '/layout.css') %]", + "[% version('/vendor/OpenLayers/theme/default/style.css') %]", + "[% version('/vendor/fancybox/jquery.fancybox-1.3.4.css') %]", + [% + FOR script IN scripts; + NEXT IF scripts_seen.${script}; + scripts_seen.${script} = 1; + ~%] + "[%- script %]", + [% END %] + "[% offline_html %]" +]; + +const staticCache = 'static'; +// const pageCache = 'pages'; + +addEventListener('install', function(evt) { + evt.waitUntil(precache()); +}); + +async function precache() { + const cache = await caches.open(staticCache); + return cache.addAll(requiredOffline); +} + +addEventListener('fetch', fetchEvent => { + const request = fetchEvent.request; + const url = new URL(request.url); + + if (url.origin !== location.origin) { + return; + } + + // Handle inspection form submission if offline... + if (request.method === 'POST' && RegExp('/report/\\d+$').test(url)) { + fetchEvent.respondWith(async function() { + const fetchPromise = fetch(request.clone()); + try { + return await fetchPromise; + } + catch { + fetchEvent.waitUntil(async function() { + var text = await request.text(); + let formData = new URLSearchParams(text); + formData.set('save', 2); + formData.set('saved_at', Math.floor(+new Date() / 1000)); + formData = formData.toString(); + + var data = await idbKeyval.get('offlineData') || { cachedReports: {}, forms: [] }; + var forms = data.forms; + if (!forms.length || formData != forms[forms.length - 1][1]) { + forms.push([request.url, formData]); + } + return idbKeyval.set('offlineData', data); + }()); + + return Response.redirect('/my/planned?saved=1'); + }; + }()); + } + + if (request.method !== "GET") { + return; + } + + fetchEvent.respondWith(async function() { + if (request.mode === 'navigate') { + const fetchPromise = fetch(request); + +// For now, only save pages manually for inspectors +// fetchEvent.waitUntil(async function() { +// const responseCopy = (await fetchPromise).clone(); +// const cache = await caches.open(pageCache); +// await responseCopy.ok ? cache.put(request, responseCopy) : cache.delete(request); +// }()); + + try { + return await fetchPromise; + } + catch { + let cached = await caches.match(request) || await caches.match("[% offline_html %]"); + return cached || offlineResponse(); + } + } else { + const responseFromCache = await caches.match(request); + return responseFromCache || fetch(request); + } + }()); +}); + +var offlineResponse = () => + new Response('Service Unavailable', { status: 503, statusText: 'Service Unavailable', headers: { 'Content-Type': 'text/html' }}); |