From d127183b9774f6321e409acdb66bf8a85d8814be Mon Sep 17 00:00:00 2001 From: Cotes Chung <11371340+cotes2020@users.noreply.github.com> Date: Sat, 4 Jun 2022 23:54:06 +0800 Subject: [PATCH] feat(PWA): add Service Worker update notification --- _data/locales/en.yml | 4 ++ _data/locales/es-ES.yml | 4 ++ _data/locales/fr-FR.yml | 4 ++ _data/locales/id-ID.yml | 4 ++ _data/locales/ko-KR.yml | 4 ++ _data/locales/my-MM.yml | 4 ++ _data/locales/pt-BR.yml | 4 ++ _data/locales/ru-RU.yml | 4 ++ _data/locales/uk-UA.yml | 4 ++ _data/locales/vi-VN.yml | 4 ++ _data/locales/zh-CN.yml | 4 ++ _layouts/default.html | 15 ++++++ _sass/addon/commons.scss | 45 ++++++++++++++++++ assets/js/pwa/app.js | 57 +++++++++++++++++++++-- assets/js/pwa/sw.js | 98 ++++++++++++++++++++-------------------- 15 files changed, 207 insertions(+), 52 deletions(-) diff --git a/_data/locales/en.yml b/_data/locales/en.yml index c4d5c79..d461f08 100644 --- a/_data/locales/en.yml +++ b/_data/locales/en.yml @@ -45,6 +45,10 @@ meta: Powered by :PLATFORM with :THEME theme. not_found: statment: Sorry, we've misplaced that URL or it's pointing to something that doesn't exist. +notification: + update_found: A new version of content is available. + update: Update + # ----- Posts related labels ----- post: diff --git a/_data/locales/es-ES.yml b/_data/locales/es-ES.yml index a1e66ab..ee3a20f 100644 --- a/_data/locales/es-ES.yml +++ b/_data/locales/es-ES.yml @@ -45,6 +45,10 @@ meta: Hecho con :PLATFORM usando el tema :THEME. not_found: statment: Lo sentimos, hemos perdido esa URL o apunta a algo que no existe. +notification: + update_found: Hay una nueva versión de contenido disponible. + update: Actualizar + # ----- Posts related labels ----- post: diff --git a/_data/locales/fr-FR.yml b/_data/locales/fr-FR.yml index 8b3cfb1..0d29a87 100644 --- a/_data/locales/fr-FR.yml +++ b/_data/locales/fr-FR.yml @@ -45,6 +45,10 @@ meta: Propulsé par :PLATFORM avec le thème :THEME not_found: statment: Désolé, nous avons égaré cette URL ou elle pointe vers quelque chose qui n'existe pas. +notification: + update_found: Une nouvelle version du contenu est disponible. + update: Mise à jour + # ----- Posts related labels ----- post: diff --git a/_data/locales/id-ID.yml b/_data/locales/id-ID.yml index 5a03406..59ec661 100644 --- a/_data/locales/id-ID.yml +++ b/_data/locales/id-ID.yml @@ -45,6 +45,10 @@ meta: Didukung oleh :PLATFORM dengan tema :THEME. not_found: statment: Maaf, kami gagal menemukan URL itu atau memang mengarah ke sesuatu yang tidak ada. +notification: + update_found: Versi konten baru tersedia. + update: Perbarui + # ----- Posts related labels ----- post: diff --git a/_data/locales/ko-KR.yml b/_data/locales/ko-KR.yml index 3580f14..2fb9391 100644 --- a/_data/locales/ko-KR.yml +++ b/_data/locales/ko-KR.yml @@ -45,6 +45,10 @@ meta: Powered by :PLATFORM with :THEME theme. not_found: statment: 해당 URL은 존재하지 않습니다. +notification: + update_found: 새 버전의 콘텐츠를 사용할 수 있습니다. + update: 업데이트 + # ----- Posts related labels ----- post: diff --git a/_data/locales/my-MM.yml b/_data/locales/my-MM.yml index 177d7db..9990c76 100644 --- a/_data/locales/my-MM.yml +++ b/_data/locales/my-MM.yml @@ -45,6 +45,10 @@ meta: Powered by :PLATFORM with :THEME theme. not_found: statment: ဝမ်းနည်းပါသည်၊ ကျွန်ုပ်တို့သည် အဆိုပါ URL ကို မှားယွင်းစွာ နေရာချထားခြင်း သို့မဟုတ် ၎င်းသည် မရှိသောအရာကို ညွှန်ပြနေပါသည်။ +notification: + update_found: အကြောင်းအရာဗားရှင်းအသစ်ကို ရနိုင်ပါပြီ။ + update: အပ်ဒိတ် + # ----- Posts related labels ----- post: diff --git a/_data/locales/pt-BR.yml b/_data/locales/pt-BR.yml index cec7bf6..59209ae 100644 --- a/_data/locales/pt-BR.yml +++ b/_data/locales/pt-BR.yml @@ -45,6 +45,10 @@ meta: Feito com :PLATFORM usando o tema :THEME. not_found: statment: Desculpe, a página não foi encontrada. +notification: + update_found: Uma nova versão do conteúdo está disponível. + update: atualização + # ----- Posts related labels ----- post: diff --git a/_data/locales/ru-RU.yml b/_data/locales/ru-RU.yml index 5e09211..3cd937c 100644 --- a/_data/locales/ru-RU.yml +++ b/_data/locales/ru-RU.yml @@ -45,6 +45,10 @@ meta: Powered by :PLATFORM with :THEME theme. not_found: statment: Извините, эта ссылка указывает на ресурс который не существует. +notification: + update_found: Доступна новая версия контента. + update: Обновлять + # ----- Posts related labels ----- post: diff --git a/_data/locales/uk-UA.yml b/_data/locales/uk-UA.yml index 566e25b..6925e42 100644 --- a/_data/locales/uk-UA.yml +++ b/_data/locales/uk-UA.yml @@ -45,6 +45,10 @@ meta: Powered by :PLATFORM with :THEME theme. not_found: statment: Вибачте, це посилання вказує на ресурс, що не існує. +notification: + update_found: Доступна нова версія вмісту. + update: Оновлення + # ----- Posts related labels ----- post: diff --git a/_data/locales/vi-VN.yml b/_data/locales/vi-VN.yml index 200baea..592000d 100644 --- a/_data/locales/vi-VN.yml +++ b/_data/locales/vi-VN.yml @@ -43,6 +43,10 @@ meta: Trang web này được tạo bởi :PLATFORM với chủ đề :THEME. not_found: statment: Xin lỗi, chúng tôi đã đặt nhầm URL hoặc đường dẫn trỏ đến một trang nào đó không tồn tại. +notification: + update_found: Đã có phiên bản mới của nội dung. + update: Cập nhật + # ----- Posts related labels ----- post: diff --git a/_data/locales/zh-CN.yml b/_data/locales/zh-CN.yml index cbdaa20..42fcfbc 100644 --- a/_data/locales/zh-CN.yml +++ b/_data/locales/zh-CN.yml @@ -44,6 +44,10 @@ meta: 本站由 :PLATFORM 生成,采用 :THEME 主题。 not_found: statment: 抱歉,我们放错了该 URL,或者它指向了不存在的内容。 +notification: + update_found: 发现新版本的内容。 + update: 更新 + # ----- Posts related labels ----- post: diff --git a/_layouts/default.html b/_layouts/default.html index 782c10a..2d34cef 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -48,6 +48,21 @@ layout: compress + + {% include search-loader.html %} {% include js-selector.html %} diff --git a/_sass/addon/commons.scss b/_sass/addon/commons.scss index e0a19a8..c9a7034 100644 --- a/_sass/addon/commons.scss +++ b/_sass/addon/commons.scss @@ -1150,6 +1150,51 @@ $sidebar-display: "sidebar-display"; -webkit-transform: translate3d(0, -5px, 0); } +#notification { + @keyframes popup { + from { + opacity: 0; + bottom: 0; + } + } + + .toast-header { + background: none; + border-bottom: none; + color: inherit; + } + + .toast-body { + font-family: 'Lato'; + line-height: 1.25rem; + + button { + font-size: 90%; + min-width: 4rem; + } + } + + &.toast { + display: none; + + &.show { + display: block; + min-width: 20rem; + border-radius: 0.5rem; + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + background-color: rgba(255, 255, 255, 0.5); + color: #1b1b1eba; + position: fixed; + left: 50%; + bottom: 20%; + transform: translateX(-50%); + animation: popup 0.8s; + } + } + +} + /* Responsive Design: diff --git a/assets/js/pwa/app.js b/assets/js/pwa/app.js index fff0cc6..fd15191 100644 --- a/assets/js/pwa/app.js +++ b/assets/js/pwa/app.js @@ -3,7 +3,56 @@ layout: compress permalink: '/app.js' --- -/* Registering Service Worker */ -if('serviceWorker' in navigator) { - navigator.serviceWorker.register('{{ "/sw.js" | relative_url }}'); -}; +const keyWaiting = 'sw-waiting'; +const $notification = $('#notification'); +const $btnRefresh = $('#notification .toast-body>button'); + +function skipWating(registration) { + registration.waiting.postMessage('SKIP_WAITING'); + localStorage.removeItem(keyWaiting); +} + +if ('serviceWorker' in navigator) { + /* Registering Service Worker */ + navigator.serviceWorker.register('{{ "/sw.js" | relative_url }}') + .then(registration => { + if (registration) { + registration.addEventListener('updatefound', () => { + /* console.log('updatefound'); */ + let serviceWorker = registration.installing; + + serviceWorker.addEventListener('statechange', () => { + /* console.log(`statechange -> ${serviceWorker.state}`); */ + if (serviceWorker.state === 'installed') { + /* console.log('installed'); */ + if (navigator.serviceWorker.controller) { + $notification.toast('show'); + /* in case the user ignores the notification */ + localStorage.setItem(keyWaiting, true); + } + } + }); + }); + + $btnRefresh.click(() => { + skipWating(registration); + $notification.toast('hide'); + }); + + /* there's a new Service Worker waiting to be activated */ + if (localStorage.getItem(keyWaiting)) { + $notification.toast('show'); + } + } + }); + + let refreshing = false; + + /* Detect controller change and refresh all the opened tabs */ + navigator.serviceWorker.addEventListener('controllerchange', () => { + if (!refreshing) { + window.location.reload(); + refreshing = true; + } + }); +} diff --git a/assets/js/pwa/sw.js b/assets/js/pwa/sw.js index 1f85e3b..7492ac3 100644 --- a/assets/js/pwa/sw.js +++ b/assets/js/pwa/sw.js @@ -6,7 +6,7 @@ permalink: '/sw.js' self.importScripts('{{ "/assets/js/data/swcache.js" | relative_url }}'); -const cacheName = 'chirpy-{{ "now" | date: "%Y%m%d.%H%M" }}'; +const cacheName = 'chirpy-{{ "now" | date: "%Y%m%d.%H%M%S" }}'; function verifyDomain(url) { for (const domain of allowedDomains) { @@ -28,60 +28,62 @@ function isExcluded(url) { return false; } -self.addEventListener('install', e => { - self.skipWaiting(); - e.waitUntil( +self.addEventListener('install', event => { + event.waitUntil( caches.open(cacheName).then(cache => { return cache.addAll(resource); }) ); }); -self.addEventListener('fetch', event => { - event.respondWith( - caches.match(event.request) - .then(response => { - if (response) { - return response; - } - - return fetch(event.request) - .then(response => { - const url = event.request.url; - - if (event.request.method !== 'GET' || - !verifyDomain(url) || - isExcluded(url)) { - return response; - } - - /* - see: - */ - let responseToCache = response.clone(); - - caches.open(cacheName) - .then(cache => { - /* console.log('[sw] Caching new resource: ' + event.request.url); */ - cache.put(event.request, responseToCache); - }); - - return response; - }); - }) - ); -}); - -self.addEventListener('activate', e => { - e.waitUntil( +self.addEventListener('activate', event => { + event.waitUntil( caches.keys().then(keyList => { - return Promise.all( - keyList.map(key => { - if(key !== cacheName) { - return caches.delete(key); - } - }) - ); + return Promise.all( + keyList.map(key => { + if (key !== cacheName) { + return caches.delete(key); + } + }) + ); + }) + ); +}); + +self.addEventListener('message', (event) => { + if (event.data === 'SKIP_WAITING') { + self.skipWaiting(); + } +}); + +self.addEventListener('fetch', event => { + event.respondWith( + caches.match(event.request).then(response => { + if (response) { + return response; + } + + return fetch(event.request).then(response => { + const url = event.request.url; + + if (event.request.method !== 'GET' || + !verifyDomain(url) || + isExcluded(url)) { + return response; + } + + /* + see: + */ + let responseToCache = response.clone(); + + caches.open(cacheName).then(cache => { + /* console.log('[sw] Caching new resource: ' + event.request.url); */ + cache.put(event.request, responseToCache); + }); + + return response; + }); }) ); });