feat(pwa): add offline tip & optimize toast style
diff --git a/.scripts/pwa/i18n.json b/.scripts/pwa/i18n.json
index 3938ce6..a184811 100644
--- a/.scripts/pwa/i18n.json
+++ b/.scripts/pwa/i18n.json
@@ -2,11 +2,13 @@
"zh": {
"Reload": "刷新",
"Close": "关闭",
- "NewContent": "有更新可用,点击”刷新“按钮获取最新内容"
+ "NewContent": "有新内容更新,点击”刷新“按钮以获取最新内容",
+ "Offline": "📴 您当前处于离线模式"
},
"en": {
"Reload": "Reload",
"Close": "Close",
- "NewContent": "New content available, click on reload button to update"
+ "NewContent": "New content available, click on reload button to update.",
+ "Offline": "📴 You are in OFFLINE mode."
}
}
\ No newline at end of file
diff --git a/.scripts/pwa/main.js b/.scripts/pwa/main.js
index 634cff5..d9e2596 100644
--- a/.scripts/pwa/main.js
+++ b/.scripts/pwa/main.js
@@ -1,7 +1,6 @@
import { registerSW } from 'virtual:pwa-register'
import i18n from './i18n.json'
-// TODO minify
-import styleCSS from './style.css?raw'
+import styleCSS from './style.css?inline'
window.addEventListener('load', () => {
const lang = i18n[window.EC_WWW_LANG || 'en']
@@ -13,27 +12,40 @@
const pwaToast = document.createElement('div')
pwaToast.className = 'pwa-toast'
pwaToast.setAttribute('role', 'alert')
- pwaToast.innerHTML = `<div class="pwa-msg">${lang['NewContent']}</div><button id="pwa-close">${lang['Close']}</button><button id="pwa-refresh">${lang['Reload']}</button>`
+ pwaToast.innerHTML = `<div class="pwa-msg"></div><button class="pwa-close">${lang['Close']}</button><button class="pwa-refresh">${lang['Reload']}</button>`
document.body.appendChild(pwaToast)
- const pwaCloseBtn = pwaToast.querySelector('#pwa-close')
- const pwaRefreshBtn = pwaToast.querySelector('#pwa-refresh')
+ const pwaMsg = pwaToast.querySelector('.pwa-msg')
+ const pwaCloseBtn = pwaToast.querySelector('.pwa-close')
+ const pwaRefreshBtn = pwaToast.querySelector('.pwa-refresh')
let refreshSW
+ let needRefresh
- const refreshCallback = () => refreshSW && refreshSW(true)
+ const refreshCallback = () => {
+ refreshSW && refreshSW(true)
+ needRefresh = false
+ }
const hideToast = () => {
pwaToast.classList.remove('show')
}
- const showToast = () => {
+ const showToast = (msg, isRefresh) => {
+ pwaMsg.innerText = msg
+ pwaToast.classList[isRefresh ? 'add' : 'remove']('is-refresh')
pwaToast.classList.add('show')
}
pwaRefreshBtn.addEventListener('click', refreshCallback)
pwaCloseBtn.addEventListener('click', hideToast)
+ const onOffline = () => needRefresh || showToast(lang['Offline'])
+ navigator.onLine || onOffline()
+
+ window.addEventListener('online', () => needRefresh || hideToast())
+ window.addEventListener('offline', onOffline)
+
refreshSW = registerSW({
immediate: true,
onOfflineReady() {
@@ -41,7 +53,8 @@
},
onNeedRefresh() {
console.log('New content available')
- showToast()
+ needRefresh = true
+ showToast(lang['NewContent'], true)
},
onRegisterError(e) {
console.error('failed to register service worker', e)
diff --git a/.scripts/pwa/style.css b/.scripts/pwa/style.css
index f919388..7ca53c0 100644
--- a/.scripts/pwa/style.css
+++ b/.scripts/pwa/style.css
@@ -2,29 +2,59 @@
visibility: hidden;
opacity: 0;
position: fixed;
- right: 0;
- bottom: 0;
- margin: 16px;
+ right: 16px;
+ bottom: 16px;
padding: 12px;
- border: 1px solid #8885;
+ max-width: 240px;
+ color: #fff;
+ background-color: rgba(0, 0, 0, .6);
+ border: 1px solid #dcdfe6;
border-radius: 4px;
- z-index: 99999;
+ -webkit-box-shadow: 0 5px 10px 0 rgba(0, 0, 0, .2);
+ box-shadow: 0 5px 10px 0 rgba(0, 0, 0, .2);
+ font-size: 14px;
+ box-sizing: border-box;
+ z-index: 9999;
text-align: left;
- box-shadow: 3px 4px 5px 0 #8885;
- background-color: #fff;
+ -webkit-transition: opacity ease .5s, visibility ease .5s;
transition: opacity ease .5s, visibility ease .5s;
}
-.pwa-toast .pwa-msg {
- margin-bottom: 8px;
-}
.pwa-toast button {
float: right;
- border: 1px solid #8885;
- outline: none;
- margin-right: 5px;
+ border: 1px solid #F72C5B;
+ margin-left: 5px;
+ margin-top: 8px;
border-radius: 2px;
- padding: 3px 10px;
- background-color: #F5F7FA;
+ padding: 2px 8px;
+ background-color: #F72C5B;
+ font-weight: 500;
+ font-size: 86%;
+ vertical-align: middle;
+ -webkit-appearance: none;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+ cursor: pointer;
+}
+.pwa-toast button,
+.pwa-toast button:active,
+.pwa-toast button:hover {
+ color: #fff;
+ outline: none;
+}
+.pwa-toast button:hover {
+ border-color: #f96b8c;
+ background-color: #f96b8c;
+}
+.pwa-toast button:active {
+ border-color: #ca274d;
+ background-color: #ca274d;
+}
+.pwa-toast .pwa-refresh {
+ display: none;
+}
+.pwa-toast.is-refresh .pwa-refresh {
+ display: block;
}
.pwa-toast.show {
visibility: visible;