diff --git a/service-worker.js b/service-worker.js index ffba823..2285ed9 100644 --- a/service-worker.js +++ b/service-worker.js @@ -1317,15 +1317,15 @@ self.addEventListener('activate', async event => { */ let still_loading_messages = { title: "Still loading", - body: "The content is still being loaded, thank you for your patience.

This page will auto-reload in a few seconds. If it does not, please click here." + body: "The resource is still being loaded, thank you for your patience.

This page will auto-reload in a few seconds. If it does not, please click here." } let success_messages = { title: "Loaded, redirecting!", - body: "The content has loaded, you are being redirected." + body: "The resource has loaded, you are being redirected." } let failure_messages = { title: "Loading failed.", - body: "We're sorry, we were unable to load this page." + body: "We're sorry, we were unable to load this resource." } /** @@ -1395,7 +1395,13 @@ let getUserFacingHTML = (init_msgs, success_msgs=false, failure_msgs=false) => { font-family: monospace; } -

${init_msgs.title}

+

${init_msgs.title}` + + if (success_msgs !== false) { + html += `` + } + + html += `

attempts: 1

${init_msgs.body}

@@ -1466,6 +1472,7 @@ let getUserFacingHTML = (init_msgs, success_msgs=false, failure_msgs=false) => { self.addEventListener('fetch', async event => { return void event.respondWith(async function () { + // initialize the SW; this is necessary as SW can be stopped at any time // and restarted when an event gets triggered -- `fetch` is just such an event. // @@ -1475,6 +1482,7 @@ self.addEventListener('fetch', async event => { // // the good news is that the config.json should have been cached already await initServiceWorker() + // if event.resultingClientId is available, we need to use this // otherwise event.clientId is what we want // ref. https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent/resultingClientId @@ -1541,82 +1549,121 @@ self.addEventListener('fetch', async event => { // get handled by plugins in case of an error let lrPromise = getResourceThroughLibResilient(url, init, clientId) - // is the stillLoadingScreen enabled, and are we navigating, or just fetching some resource? - if ( ( self.LibResilientConfig.stillLoadingTimeout > 0 ) && ( event.request.mode === 'navigate' ) ) { + // are we navigating, or just fetching some resource? + if ( event.request.mode === 'navigate' ) { - self.log('service-worker', `handling a navigate request; still-loading timeout: ${self.LibResilientConfig.stillLoadingTimeout}.`) + // this is the promise we will want to return in the end + let finalPromise = null - let slPromise, slTimeoutId - [slPromise, slTimeoutId] = promiseTimeout(self.LibResilientConfig.stillLoadingTimeout, true) + // navigating! is the still-loading screen enabled? + if ( self.LibResilientConfig.stillLoadingTimeout > 0 ) { - // make sure to clear the timeout related to slPromise - // in case we manage to get the content through the plugins - lrPromise - .then(()=>{ - self.log('service-worker', `content retrieved; still-loading timeout cleared.`) - clearTimeout(slTimeoutId) - }) + // it is enabled! + self.log('service-worker', `handling a navigate request; still-loading timeout: ${self.LibResilientConfig.stillLoadingTimeout}.`) + + let slPromise, slTimeoutId + [slPromise, slTimeoutId] = promiseTimeout(self.LibResilientConfig.stillLoadingTimeout, true) + + // make sure to clear the timeout related to slPromise + // in case we manage to get the content through the plugins + lrPromise + .then(()=>{ + self.log('service-worker', `content retrieved; still-loading timeout cleared.`) + clearTimeout(slTimeoutId) + }) + + // prepare a Promise that races the "still loading" screen promise against the LibResilient plugins + finalPromise = Promise.race([ + // regular fetch-through-plugins + lrPromise, + + // the "still loading screen" + // + // this will delay a specified time, and ten return a Response + // with very basic HTML informing the user that the page is still loading, + // a Refresh header set, and a link for the user to reload the screen manually + slPromise + .then(()=>{ + + // inform + self.log('service-worker', 'handling a navigate request is taking too long, showing the still-loading screen') + + // we need to create a new Response object + // with all the headers added explicitly, + // since response.headers is immutable + var responseInit = { + status: 202, + statusText: "Accepted", + headers: {}, + url: url + }; + responseInit.headers['Content-Type'] = "text/html" + responseInit.headers['X-LibResilient-Method'] = "still-loading" + + // get the still-loading page contents + let stillLoadingHTML = getUserFacingHTML( + still_loading_messages, + success_messages, + failure_messages + ) + + let blob = new Blob( + [stillLoadingHTML], + {type: "text/html"} + ) + + return new Response( + blob, + responseInit + ) + }) + ]) + + // okay, no still-loading screen, but this is still a navigate request, so we want to display something to the user + } else { + self.log('service-worker', `handling a navigate request, but still-loading screen is disabled.`) + finalPromise = lrPromise + } - // return a Promise that races the "still loading" screen promise against the LibResilient plugins - return Promise.race([ - // regular fetch-through-plugins - lrPromise, - - // the "still loading screen" - // - // this will delay a specified time, and ten return a Response - // with very basic HTML informing the user that the page is still loading, - // a Refresh header set, and a link for the user to reload the screen manually - slPromise - .then(()=>{ - - // inform - self.log('service-worker', 'handling a navigate request is taking too long, showing the still-loading screen') - - // we need to create a new Response object - // with all the headers added explicitly, - // since response.headers is immutable - var responseInit = { - status: 202, - statusText: "Accepted", - headers: {}, - url: url - }; - responseInit.headers['Content-Type'] = "text/html" - // refresh: we want a minimum of 1s; stillLoadingTimeout is in ms! - //responseInit.headers['Refresh'] = Math.ceil( self.LibResilientConfig.stillLoadingTimeout / 1000 ) - //responseInit.headers['ETag'] = ??? - //responseInit.headers['X-LibResilient-ETag'] = ??? - responseInit.headers['X-LibResilient-Method'] = "still-loading" - - // get the still-loading page contents - let stillLoadingHTML = getUserFacingHTML( - still_loading_messages, - success_messages, - failure_messages - ) - - let blob = new Blob( - [stillLoadingHTML], - {type: "text/html"} - ) - - return new Response( - blob, - responseInit - ) - }) - ]) + // return finalPromise, with a catch! + return finalPromise.catch((e)=>{ + // inform + self.log('service-worker', 'handling a failed navigate request, showing the user-facing error screen') + + // we need to create a new Response object + // with all the headers added explicitly, + // since response.headers is immutable + var responseInit = { + status: 404, + statusText: "Not Found", + headers: {}, + url: url + }; + responseInit.headers['Content-Type'] = "text/html" + responseInit.headers['X-LibResilient-Method'] = "failed" + + // get the still-loading page contents + // it only needs to display the failure messages + let stillLoadingHTML = getUserFacingHTML( + failure_messages + ) + + let blob = new Blob( + [stillLoadingHTML], + {type: "text/html"} + ) + + return new Response( + blob, + responseInit + ) + }) // nope, just fetching a resource } else { - if ( event.request.mode === 'navigate' ) { - self.log('service-worker', `handling a navigate request, but still-loading screen is disabled.`) - } else { - self.log('service-worker', 'handling a regular request; still-loading screen will not be used.') - } - // no need for the whole "still loading screen" flow - return lrPromise; + // no need for a still-loading screen, no need for an user-facing error screen if the request fails + self.log('service-worker', 'handling a regular request; still-loading screen will not be used.') + return lrPromise } }()) });