From 0ad07fd6c8ff0d68b7597d77ea9c79cf1a3f7d94 Mon Sep 17 00:00:00 2001 From: lassejaco Date: Thu, 11 Apr 2024 15:30:23 +0200 Subject: [PATCH] pwa offline improvements --- devcon-app/next.config.js | 11 +++ devcon-app/runtime-cache.js | 184 ++++++++++++++++++++++++++++++++++++ devcon-app/workbox/index.ts | 40 ++++---- 3 files changed, 215 insertions(+), 20 deletions(-) create mode 100644 devcon-app/runtime-cache.js diff --git a/devcon-app/next.config.js b/devcon-app/next.config.js index 36f96766a..6ee8d06d5 100644 --- a/devcon-app/next.config.js +++ b/devcon-app/next.config.js @@ -7,6 +7,7 @@ const getStaticPrecacheEntries = require('./precache-public') const { withSentryConfig } = require('@sentry/nextjs') const path = require('path') const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin +const runtimeCache = require('./runtime-cache') /** @type {import('next').NextConfig} */ const nextConfig = { @@ -162,6 +163,16 @@ const createConfig = phase => { cacheOnFrontEndNav: true, ignoreURLParametersMatching: [/^session/, /^speaker/, /^room/, /^floor/], buildExcludes: [/media\/.*$/, /\.map$/], + runtimeCaching: [ + ...runtimeCache, + { + urlPattern: /^https:\/\/api\.devcon\.org\.com\/.*/i, + handler: 'StaleWhileRevalidate', + options: { + cacheName: 'devcon-api', + }, + }, + ], // fallbacks: { // image: // 'https://images.unsplash.com/photo-1589652717521-10c0d092dea9?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1470&q=80', diff --git a/devcon-app/runtime-cache.js b/devcon-app/runtime-cache.js new file mode 100644 index 000000000..a018df28e --- /dev/null +++ b/devcon-app/runtime-cache.js @@ -0,0 +1,184 @@ +'use strict' + +// Workbox RuntimeCaching config: https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-build#.RuntimeCachingEntry +module.exports = [ + { + urlPattern: /^https:\/\/fonts\.(?:gstatic)\.com\/.*/i, + handler: 'CacheFirst', + options: { + cacheName: 'google-fonts-webfonts', + expiration: { + maxEntries: 4, + maxAgeSeconds: 365 * 24 * 60 * 60, // 365 days + }, + }, + }, + { + urlPattern: /^https:\/\/fonts\.(?:googleapis)\.com\/.*/i, + handler: 'StaleWhileRevalidate', + options: { + cacheName: 'google-fonts-stylesheets', + expiration: { + maxEntries: 4, + maxAgeSeconds: 7 * 24 * 60 * 60, // 7 days + }, + }, + }, + { + urlPattern: /\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i, + handler: 'StaleWhileRevalidate', + options: { + cacheName: 'static-font-assets', + expiration: { + maxEntries: 4, + maxAgeSeconds: 7 * 24 * 60 * 60, // 7 days + }, + }, + }, + { + urlPattern: /\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i, + handler: 'StaleWhileRevalidate', + options: { + cacheName: 'static-image-assets', + expiration: { + maxEntries: 64, + maxAgeSeconds: 24 * 60 * 60, // 24 hours + }, + }, + }, + { + urlPattern: /\/_next\/image\?url=.+$/i, + handler: 'StaleWhileRevalidate', + options: { + cacheName: 'next-image', + expiration: { + maxEntries: 64, + maxAgeSeconds: 24 * 60 * 60, // 24 hours + }, + }, + }, + { + urlPattern: /\.(?:mp3|wav|ogg)$/i, + handler: 'CacheFirst', + options: { + rangeRequests: true, + cacheName: 'static-audio-assets', + expiration: { + maxEntries: 32, + maxAgeSeconds: 24 * 60 * 60, // 24 hours + }, + }, + }, + { + urlPattern: /\.(?:mp4)$/i, + handler: 'CacheFirst', + options: { + rangeRequests: true, + cacheName: 'static-video-assets', + expiration: { + maxEntries: 32, + maxAgeSeconds: 24 * 60 * 60, // 24 hours + }, + }, + }, + { + urlPattern: /\.(?:js)$/i, + handler: 'StaleWhileRevalidate', + options: { + cacheName: 'static-js-assets', + expiration: { + maxEntries: 32, + maxAgeSeconds: 24 * 60 * 60, // 24 hours + }, + }, + }, + { + urlPattern: /\.(?:css|less)$/i, + handler: 'StaleWhileRevalidate', + options: { + cacheName: 'static-style-assets', + expiration: { + maxEntries: 32, + maxAgeSeconds: 24 * 60 * 60, // 24 hours + }, + }, + }, + { + urlPattern: /\/_next\/data\/.+\/.+\.json$/i, + handler: 'StaleWhileRevalidate', + options: { + cacheName: 'next-data', + expiration: { + maxEntries: 32, + maxAgeSeconds: 24 * 60 * 60, // 24 hours + }, + }, + }, + { + urlPattern: /\.(?:json|xml|csv)$/i, + handler: 'NetworkFirst', + options: { + cacheName: 'static-data-assets', + expiration: { + maxEntries: 32, + maxAgeSeconds: 24 * 60 * 60, // 24 hours + }, + }, + }, + { + urlPattern: ({ url }) => { + const isSameOrigin = self.origin === url.origin + if (!isSameOrigin) return false + const pathname = url.pathname + // Exclude /api/auth/callback/* to fix OAuth workflow in Safari without impact other environment + // Above route is default for next-auth, you may need to change it if your OAuth workflow has a different callback route + // Issue: https://github.com/shadowwalker/next-pwa/issues/131#issuecomment-821894809 + if (pathname.startsWith('/api/auth/')) return false + if (pathname.startsWith('/api/')) return true + return false + }, + handler: 'NetworkFirst', + method: 'GET', + options: { + cacheName: 'apis', + expiration: { + maxEntries: 16, + maxAgeSeconds: 24 * 60 * 60, // 24 hours + }, + networkTimeoutSeconds: 10, // fall back to cache if api does not response within 10 seconds + }, + }, + { + urlPattern: ({ url }) => { + const isSameOrigin = self.origin === url.origin + if (!isSameOrigin) return false + const pathname = url.pathname + if (pathname.startsWith('/api/')) return false + return true + }, + handler: 'NetworkFirst', + options: { + cacheName: 'others', + expiration: { + maxEntries: 32, + maxAgeSeconds: 24 * 60 * 60, // 24 hours + }, + networkTimeoutSeconds: 10, + }, + }, + { + urlPattern: ({ url }) => { + const isSameOrigin = self.origin === url.origin + return !isSameOrigin + }, + handler: 'NetworkFirst', + options: { + cacheName: 'cross-origin', + expiration: { + maxEntries: 32, + maxAgeSeconds: 60 * 60, // 1 hour + }, + networkTimeoutSeconds: 10, + }, + }, +] diff --git a/devcon-app/workbox/index.ts b/devcon-app/workbox/index.ts index 5d21cd2e1..e8a1f86a2 100644 --- a/devcon-app/workbox/index.ts +++ b/devcon-app/workbox/index.ts @@ -1,4 +1,4 @@ -const _self = self as unknown as ServiceWorkerGlobalScope +// const _self = self as unknown as ServiceWorkerGlobalScope // To disable all workbox logging during development, you can set self.__WB_DISABLE_DEV_LOGS to true // https://developers.google.com/web/tools/workbox/guides/configure-workbox#disable_logging @@ -19,26 +19,26 @@ const _self = self as unknown as ServiceWorkerGlobalScope // // console.log(process.env, 'env') // }) -_self.addEventListener("fetch", (e: any) => { - console.log('FETCH', e.request.url) - // const controlledRoutes = ['/schedule', '/speakers', '/rooms']; - const requestURL = e.request.url; - // const controlledRoute = controlledRoutes.find(route => requestURL.includes(route)); +// _self.addEventListener("fetch", (e: any) => { +// console.log('FETCH', e.request.url) +// // const controlledRoutes = ['/schedule', '/speakers', '/rooms']; +// const requestURL = e.request.url; +// // const controlledRoute = controlledRoutes.find(route => requestURL.includes(route)); - if (requestURL.includes('api.devcon.org')) { - // const urlWithNoQuery = requestURL.split('?')[0]; +// if (requestURL.includes('api.devcon.org')) { +// // const urlWithNoQuery = requestURL.split('?')[0]; - // e.respondWith(fetch(urlWithNoQuery)); - e.respondWith(caches.match(e.request).then(response => { - if (response) { - console.log('[demoPWA - ServiceWorker] Retrieving from cache...'); - return response; - } - console.log('[demoPWA - ServiceWorker] Retrieving from URL...'); - return fetch(e.request); - })) - // ); - } +// // e.respondWith(fetch(urlWithNoQuery)); +// e.respondWith(caches.match(e.request).then(response => { +// if (response) { +// console.log('[demoPWA - ServiceWorker] Retrieving from cache...'); +// return response; +// } +// console.log('[demoPWA - ServiceWorker] Retrieving from URL...'); +// return fetch(e.request); +// })) +// // ); +// } // e.respondWith( // caches.match(e.request).then(function(response) { @@ -50,7 +50,7 @@ _self.addEventListener("fetch", (e: any) => { // return fetch(e.request); // }) // ); -}); +// }); // _self.addEventListener('activate', event => { // console.log('activated')