From 5ea1357a9933d67a4e7bd9cef5cccfe79b3524f1 Mon Sep 17 00:00:00 2001 From: Patrick Brosset Date: Thu, 29 Sep 2022 15:18:47 +0200 Subject: [PATCH] Offline support for wami --- wami/README.md | 2 +- wami/sw.js | 114 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 113 insertions(+), 3 deletions(-) diff --git a/wami/README.md b/wami/README.md index 1572165..9cec1a2 100644 --- a/wami/README.md +++ b/wami/README.md @@ -37,5 +37,5 @@ The app uses the [WASM-ImageMagick](https://github.com/KnicKnic/WASM-ImageMagick ## TODO * Categorize steps and add a way to search through them. -* Implement real sw.js. * Share images. +* Export/import flows as JSON files. diff --git a/wami/sw.js b/wami/sw.js index dc87cc3..e79ab76 100644 --- a/wami/sw.js +++ b/wami/sw.js @@ -1,3 +1,113 @@ -self.addEventListener("fetch", event => { - const url = new URL(event.request.url); +const VERSION = 'v1'; +const CACHE_NAME = `wami-${VERSION}`; + +// Those are all the resources our app needs to work. +// We'll cache them on install. +const INITIAL_CACHED_RESOURCES = [ + './', + // CSS + './styles/buttons.css', + './styles/dialogs.css', + './styles/editor.css', + './styles/image-viewer.css', + './styles/images.css', + './styles/index.css', + './styles/layout.css', + './styles/list-of-flows.css', + './styles/reset.css', + './styles/step-chooser.css', + './styles/welcome.css', + // JS + './app.js', + './flow-runner.js', + './ui.js', + './store.js', + './utils.js', + './image-viewer.js', + './steps.js', + // 3rd party JS + 'https://cdn.jsdelivr.net/npm/wasm-imagemagick/dist/bundles/magickApi.js', + // Images + './icons/add-images.png', + './icons/step-blur.png', + './icons/step-border.png', + './icons/step-brightness-contrast.png', + './icons/step-clone.png', + './icons/step-colorize.png', + './icons/step-crop.png', + './icons/step-edge.png', + './icons/step-flip.png', + './icons/step-flop.png', + './icons/step-grayscale.png', + './icons/step-negate.png', + './icons/step-paint.png', + './icons/step-polaroid.png', + './icons/step-posterize.png', + './icons/step-resize-exactly.png', + './icons/step-resize-height-if-larger.png', + './icons/step-resize-width-if-larger.png', + './icons/step-resize.png', + './icons/step-rotate.png', + './icons/step-scale.png', + './icons/step-sepia-tone.png', + './icons/step-sharpen.png', + './favicon-96.png', + './favicon-128.png', + './favicon-256.png', + './favicon-512.png', +]; + +// Add a cache-busting query string to the pre-cached resources. +// This is to avoid loading these resources from the disk cache. +const INITIAL_CACHED_RESOURCES_WITH_VERSIONS = INITIAL_CACHED_RESOURCES.map(path => { + return `${path}?v=${VERSION}`; +}); + +// On install, fill the cache with all the resources we know we need. +// Install happens when the app is used for the first time, or when a +// new version of the SW is detected by the browser. +// In the latter case, the old SW is kept around until the new one is +// activated by a new client. +self.addEventListener('install', event => { + self.skipWaiting(); + + event.waitUntil((async () => { + const cache = await caches.open(CACHE_NAME); + cache.addAll(INITIAL_CACHED_RESOURCES_WITH_VERSIONS); + })()); +}); + +// Activate happens after install, either when the app is used for the +// first time, or when a new version of the SW was installed. +// We use the activate event to delete old caches and avoid running out of space. +self.addEventListener('activate', event => { + event.waitUntil((async () => { + const names = await caches.keys(); + await Promise.all(names.map(name => { + if (name !== CACHE_NAME) { + return caches.delete(name); + } + })); + await clients.claim(); + })()); +}); + +// Main fetch handler. +// A cache-first strategy is used, with a fallback to the network. +// The static resources fetched here will not have the cache-busting query +// string. So we need to add it to match the cache. +self.addEventListener('fetch', event => { + // On fetch, go to the cache first, and then network. + event.respondWith((async () => { + const cache = await caches.open(CACHE_NAME); + const cachedResponse = await cache.match(`${event.request.url}?v=${VERSION}`); + + if (cachedResponse) { + return cachedResponse; + } else { + const fetchResponse = await fetch(event.request); + cache.put(event.request, fetchResponse.clone()); + return fetchResponse; + } + })()); });