diff --git a/frontend/controller/service-worker.js b/frontend/controller/service-worker.js index 574fa4d42..5bdbf302f 100644 --- a/frontend/controller/service-worker.js +++ b/frontend/controller/service-worker.js @@ -51,6 +51,34 @@ sbp('sbp/selectors/register', { await swRegistration.update() setInterval(() => sbp('service-worker/update'), HOURS_MILLIS) + // Send a 'ready' message to the SW and wait back for a response + // This way we ensure that Chelonia has been set up + await new Promise((resolve, reject) => { + const messageChannel = new MessageChannel() + messageChannel.port1.onmessage = (event) => { + if (event.data.type === 'ready') { + resolve() + } else { + reject(event.data.error) + } + messageChannel.port1.close() + } + messageChannel.port1.onmessageerror = () => { + reject(new Error('Message error')) + messageChannel.port1.close() + } + + navigator.serviceWorker.ready.then(() => { + navigator.serviceWorker.controller.postMessage({ + type: 'ready', + port: messageChannel.port2 + }, [messageChannel.port2]) + }).catch((e) => { + reject(e) + messageChannel.port1.close() + }) + }) + // Keep the service worker alive while the window is open // The default idle timeout on Chrome and Firefox is 30 seconds. We send // a ping message every 5 seconds to ensure that the worker remains diff --git a/frontend/controller/serviceworkers/sw-primary.js b/frontend/controller/serviceworkers/sw-primary.js index 3c3f9b83d..c4e3e7910 100644 --- a/frontend/controller/serviceworkers/sw-primary.js +++ b/frontend/controller/serviceworkers/sw-primary.js @@ -244,6 +244,26 @@ self.addEventListener('message', function (event) { case 'event': sbp('okTurtles.events/emit', event.data.subtype, ...deserializer(event.data.data)) break + case 'ready': { + // The 'ready' message is sent by a client (i.e., a tab or window) to + // ensure that Chelonia has been setup + const port = event.data.port + Promise.race([ + setupChelonia(), + new Promise((resolve, reject) => { + setTimeout(() => { + reject(new Error('Timed out setting up Chelonia')) + }, 30e3) + }) + ]).then(() => { + port.postMessage({ type: 'ready' }) + }, (e) => { + port.postMessage({ type: 'error', error: e }) + }).finally(() => { + port.close() + }) + break + } default: console.error('[sw] unknown message type:', event.data) break diff --git a/frontend/setupChelonia.js b/frontend/setupChelonia.js index 625c3880f..4b3971e6b 100644 --- a/frontend/setupChelonia.js +++ b/frontend/setupChelonia.js @@ -310,6 +310,7 @@ export default ((() => { return () => { if (!promise) { promise = setupChelonia().catch((e) => { + console.error('[setupChelonia] Error during chelonia setup', e) promise = undefined // Reset on error throw e // Re-throw the error })