From 3aa50319705ceefc4db1280ec068c3e896b9c40c Mon Sep 17 00:00:00 2001 From: xiaoiver Date: Fri, 16 Nov 2018 15:47:25 +0800 Subject: [PATCH] feat: support PWA features (#2816) * feat: support PWA features * fix: move to global.js --- config/config.js | 6 ++++ public/icons/icon-128x128.png | Bin 0 -> 1329 bytes public/icons/icon-192x192.png | Bin 0 -> 1856 bytes src/global.js | 39 ++++++++++++++++++++ src/layouts/BasicLayout.js | 4 +-- src/locales/en-US.js | 2 ++ src/locales/en-US/pwa.js | 6 ++++ src/locales/pt-BR.js | 2 ++ src/locales/pt-BR/pwa.js | 7 ++++ src/locales/zh-CN.js | 2 ++ src/locales/zh-CN/pwa.js | 6 ++++ src/locales/zh-TW.js | 2 ++ src/locales/zh-TW/pwa.js | 6 ++++ src/manifest.json | 17 +++++++++ src/models/login.js | 2 +- src/service-worker.js | 65 ++++++++++++++++++++++++++++++++++ 16 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 public/icons/icon-128x128.png create mode 100644 public/icons/icon-192x192.png create mode 100644 src/global.js create mode 100644 src/locales/en-US/pwa.js create mode 100644 src/locales/pt-BR/pwa.js create mode 100644 src/locales/zh-CN/pwa.js create mode 100644 src/locales/zh-TW/pwa.js create mode 100644 src/manifest.json create mode 100644 src/service-worker.js diff --git a/config/config.js b/config/config.js index 337a53b0..c7910ea6 100644 --- a/config/config.js +++ b/config/config.js @@ -23,6 +23,12 @@ const plugins = [ dynamicImport: { loadingComponent: './components/PageLoading/index', }, + pwa: { + workboxPluginMode: 'InjectManifest', + workboxOptions: { + importWorkboxFrom: 'local', + }, + }, ...(!process.env.TEST && os.platform() === 'darwin' ? { dll: { diff --git a/public/icons/icon-128x128.png b/public/icons/icon-128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..48d0e2339a60a637b94319c65e8654289b4f4b6c GIT binary patch literal 1329 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 { + message.warning(formatMessage({ id: 'app.pwa.offline' })); +}); + +// Pop up a prompt on the page asking the user if they want to use the latest version +window.addEventListener('sw.updated', e => { + Modal.confirm({ + title: formatMessage({ id: 'app.pwa.serviceworker.updated' }), + content: formatMessage({ id: 'app.pwa.serviceworker.updated.hint' }), + okText: formatMessage({ id: 'app.pwa.serviceworker.updated.ok' }), + onOk: async () => { + // Check if there is sw whose state is waiting in ServiceWorkerRegistration + // https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration + const worker = e.detail && e.detail.waiting; + if (!worker) { + return Promise.resolve(); + } + // Send skip-waiting event to waiting SW with MessageChannel + await new Promise((resolve, reject) => { + const channel = new MessageChannel(); + channel.port1.onmessage = event => { + if (event.data.error) { + reject(event.data.error); + } else { + resolve(event.data); + } + }; + worker.postMessage({ type: 'skip-waiting' }, [channel.port2]); + }); + // Refresh current page to use the updated HTML and other assets after SW has skiped waiting + window.location.reload(true); + return true; + }, + }); +}); diff --git a/src/layouts/BasicLayout.js b/src/layouts/BasicLayout.js index 96b34ac2..06c403ea 100644 --- a/src/layouts/BasicLayout.js +++ b/src/layouts/BasicLayout.js @@ -180,11 +180,11 @@ class BasicLayout extends React.PureComponent { if (!currRouterData) { return 'Ant Design Pro'; } - const message = formatMessage({ + const pageName = formatMessage({ id: currRouterData.locale || currRouterData.name, defaultMessage: currRouterData.name, }); - return `${message} - Ant Design Pro`; + return `${pageName} - Ant Design Pro`; }; getLayoutStyle = () => { diff --git a/src/locales/en-US.js b/src/locales/en-US.js index 2d61a22d..fcaffceb 100644 --- a/src/locales/en-US.js +++ b/src/locales/en-US.js @@ -8,6 +8,7 @@ import monitor from './en-US/monitor'; import result from './en-US/result'; import settingDrawer from './en-US/settingDrawer'; import settings from './en-US/settings'; +import pwa from './en-US/pwa'; export default { 'navBar.lang': 'Languages', @@ -28,4 +29,5 @@ export default { ...result, ...settingDrawer, ...settings, + ...pwa, }; diff --git a/src/locales/en-US/pwa.js b/src/locales/en-US/pwa.js new file mode 100644 index 00000000..ed8d199e --- /dev/null +++ b/src/locales/en-US/pwa.js @@ -0,0 +1,6 @@ +export default { + 'app.pwa.offline': 'You are offline now', + 'app.pwa.serviceworker.updated': 'New content is available', + 'app.pwa.serviceworker.updated.hint': 'Please press the "Refresh" button to reload current page', + 'app.pwa.serviceworker.updated.ok': 'Refresh', +}; diff --git a/src/locales/pt-BR.js b/src/locales/pt-BR.js index 6ee728e3..45df10bb 100644 --- a/src/locales/pt-BR.js +++ b/src/locales/pt-BR.js @@ -8,6 +8,7 @@ import monitor from './pt-BR/monitor'; import result from './pt-BR/result'; import settingDrawer from './pt-BR/settingDrawer'; import settings from './pt-BR/settings'; +import pwa from './pt-BR/pwa'; export default { 'navBar.lang': 'Idiomas', @@ -28,4 +29,5 @@ export default { ...result, ...settingDrawer, ...settings, + ...pwa, }; diff --git a/src/locales/pt-BR/pwa.js b/src/locales/pt-BR/pwa.js new file mode 100644 index 00000000..05cc7978 --- /dev/null +++ b/src/locales/pt-BR/pwa.js @@ -0,0 +1,7 @@ +export default { + 'app.pwa.offline': 'Você está offline agora', + 'app.pwa.serviceworker.updated': 'Novo conteúdo está disponível', + 'app.pwa.serviceworker.updated.hint': + 'Por favor, pressione o botão "Atualizar" para recarregar a página atual', + 'app.pwa.serviceworker.updated.ok': 'Atualizar', +}; diff --git a/src/locales/zh-CN.js b/src/locales/zh-CN.js index 0d25463a..4330d60a 100644 --- a/src/locales/zh-CN.js +++ b/src/locales/zh-CN.js @@ -8,6 +8,7 @@ import monitor from './zh-CN/monitor'; import result from './zh-CN/result'; import settingDrawer from './zh-CN/settingDrawer'; import settings from './zh-CN/settings'; +import pwa from './zh-CN/pwa'; export default { 'navBar.lang': '语言', @@ -28,4 +29,5 @@ export default { ...result, ...settingDrawer, ...settings, + ...pwa, }; diff --git a/src/locales/zh-CN/pwa.js b/src/locales/zh-CN/pwa.js new file mode 100644 index 00000000..e9504849 --- /dev/null +++ b/src/locales/zh-CN/pwa.js @@ -0,0 +1,6 @@ +export default { + 'app.pwa.offline': '当前处于离线状态', + 'app.pwa.serviceworker.updated': '有新内容', + 'app.pwa.serviceworker.updated.hint': '请点击“刷新”按钮或者手动刷新页面', + 'app.pwa.serviceworker.updated.ok': '刷新', +}; diff --git a/src/locales/zh-TW.js b/src/locales/zh-TW.js index b78d64e3..986820ef 100644 --- a/src/locales/zh-TW.js +++ b/src/locales/zh-TW.js @@ -8,6 +8,7 @@ import monitor from './zh-TW/monitor'; import result from './zh-TW/result'; import settingDrawer from './zh-TW/settingDrawer'; import settings from './zh-TW/settings'; +import pwa from './zh-TW/pwa'; export default { 'navBar.lang': '語言', @@ -28,4 +29,5 @@ export default { ...result, ...settingDrawer, ...settings, + ...pwa, }; diff --git a/src/locales/zh-TW/pwa.js b/src/locales/zh-TW/pwa.js new file mode 100644 index 00000000..108a6e48 --- /dev/null +++ b/src/locales/zh-TW/pwa.js @@ -0,0 +1,6 @@ +export default { + 'app.pwa.offline': '當前處於離線狀態', + 'app.pwa.serviceworker.updated': '有新內容', + 'app.pwa.serviceworker.updated.hint': '請點擊“刷新”按鈕或者手動刷新頁面', + 'app.pwa.serviceworker.updated.ok': '刷新', +}; diff --git a/src/manifest.json b/src/manifest.json new file mode 100644 index 00000000..69bce982 --- /dev/null +++ b/src/manifest.json @@ -0,0 +1,17 @@ +{ + "name": "ant-design-pro", + "short_name": "antd-pro", + "display": "standalone", + "start_url": "./?utm_source=homescreen", + "theme_color": "#002140", + "background_color": "#001529", + "icons": [ + { + "src": "icons/icon-192x192.png", + "sizes": "192x192" + },{ + "src": "icons/icon-128x128.png", + "sizes": "128x128" + } + ] +} \ No newline at end of file diff --git a/src/models/login.js b/src/models/login.js index fffc5197..82fc4249 100644 --- a/src/models/login.js +++ b/src/models/login.js @@ -30,7 +30,7 @@ export default { if (redirectUrlParams.origin === urlParams.origin) { redirect = redirect.substr(urlParams.origin.length); if (redirect.match(/^\/.*#/)) { - redirect = redirect.substr(redirect.indexOf('#')+1); + redirect = redirect.substr(redirect.indexOf('#') + 1); } } else { window.location.href = redirect; diff --git a/src/service-worker.js b/src/service-worker.js new file mode 100644 index 00000000..48d43c25 --- /dev/null +++ b/src/service-worker.js @@ -0,0 +1,65 @@ +/* globals workbox */ +/* eslint-disable no-restricted-globals */ +workbox.core.setCacheNameDetails({ + prefix: 'antd-pro', + suffix: 'v1', +}); +// Control all opened tabs ASAP +workbox.clientsClaim(); + +/** + * Use precaching list generated by workbox in build process. + * https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.precaching + */ +/* eslint-disable no-underscore-dangle */ +workbox.precaching.precacheAndRoute(self.__precacheManifest || []); + +/** + * Register a navigation route. + * https://developers.google.com/web/tools/workbox/modules/workbox-routing#how_to_register_a_navigation_route + */ +workbox.routing.registerNavigationRoute('/index.html'); + +/** + * Use runtime cache: + * https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.routing#.registerRoute + * + * Workbox provides all common caching strategies including CacheFirst, NetworkFirst etc. + * https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.strategies + */ + +/** + * Handle API requests + */ +workbox.routing.registerRoute(/\/api\//, workbox.strategies.networkFirst()); + +/** + * Handle third party requests + */ +workbox.routing.registerRoute( + /^https:\/\/gw.alipayobjects.com\//, + workbox.strategies.networkFirst() +); +workbox.routing.registerRoute( + /^https:\/\/cdnjs.cloudflare.com\//, + workbox.strategies.networkFirst() +); +workbox.routing.registerRoute(/\/color.less/, workbox.strategies.networkFirst()); + +/** + * Response to client after skipping waiting with MessageChannel + */ +addEventListener('message', event => { + const replyPort = event.ports[0]; + const message = event.data; + if (replyPort && message && message.type === 'skip-waiting') { + event.waitUntil( + self + .skipWaiting() + .then( + () => replyPort.postMessage({ error: null }), + error => replyPort.postMessage({ error }) + ) + ); + } +}); -- GitLab