diff --git a/build.js b/build.js
index 4bf2024..6f07e85 100644
--- a/build.js
+++ b/build.js
@@ -5,7 +5,9 @@ const shell = require('shelljs')
shell.rm('-rf', 'dist')
require('webpack')(require('./webpack.config.js'), (err, stats) => {
- if (err) throw err
+ if (err) {
+ throw err
+ }
process.stdout.write(stats.toString({
colors: true,
modules: false,
diff --git a/package.json b/package.json
index f0781fa..a9274b2 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
"vuex-router-sync": "^3.0.0"
},
"devDependencies": {
+ "appcache-webpack-plugin": "^1.3.0",
"autoprefixer": "^6.4.0",
"babel-core": "^6.16.0",
"babel-helper-vue-jsx-merge-props": "^2.0.1",
@@ -55,6 +56,7 @@
"postcss-import": "^9.1.0",
"postcss-loader": "^0.9.1",
"raw-loader": "^0.5.1",
+ "serviceworker-webpack-plugin": "^0.1.8",
"shelljs": "^0.7.6",
"style-loader": "^0.13.1",
"svg-sprite-loader": "^0.2.0",
diff --git a/src/components/DeckFloatButton.vue b/src/components/DeckFloatButton.vue
index af6e6e3..202904a 100644
--- a/src/components/DeckFloatButton.vue
+++ b/src/components/DeckFloatButton.vue
@@ -57,7 +57,7 @@ export default {
diff --git a/src/index.js b/src/index.js
index 22904ef..e5704d9 100644
--- a/src/index.js
+++ b/src/index.js
@@ -30,3 +30,8 @@ $get('./CardInfo.json')
})
}, 1000)
})
+
+import runtime from 'serviceworker-webpack-plugin/lib/runtime'
+if ('serviceWorker' in navigator) {
+ runtime.register()
+}
diff --git a/src/index.tpl b/src/index.tpl
index 70bfca5..2eab931 100644
--- a/src/index.tpl
+++ b/src/index.tpl
@@ -1,5 +1,5 @@
-
+
diff --git a/src/pages/Deck.vue b/src/pages/Deck.vue
index 5cc3efd..c89b0f7 100644
--- a/src/pages/Deck.vue
+++ b/src/pages/Deck.vue
@@ -3,6 +3,7 @@ import { mapState, mapGetters } from 'vuex'
import { AppHeader, HeaderIcon, HeaderMenu } from 'components/AppHeader'
import DeckModals from 'components/DeckModals'
import DeckFloatButton from 'components/DeckFloatButton'
+import UpdateStatusBar from 'components/UpdateStatusBar'
import Cell from 'components/Cell'
import Block from 'components/Block'
import DeckHead from 'components/DeckHead'
@@ -20,6 +21,7 @@ export default {
HeaderMenu,
DeckModals,
DeckFloatButton,
+ UpdateStatusBar,
Cell,
Block,
DeckHead,
@@ -203,9 +205,25 @@ export default {
-
+
+
+
+
+
+
@@ -225,4 +243,14 @@ export default {
.block {
width: 20%;
}
-
\ No newline at end of file
+.bottom {
+ position: fixed;
+ left: 0;
+ right: 0;
+ bottom: 0;
+
+ & > div {
+ position: relative;
+ }
+}
+
diff --git a/src/service-worker.js b/src/service-worker.js
new file mode 100644
index 0000000..bedd47e
--- /dev/null
+++ b/src/service-worker.js
@@ -0,0 +1,105 @@
+'use strict'
+
+let hash = self.serviceWorkerOption.hash
+let assets = self.serviceWorkerOption.assets
+let staticAssets = assets.filter(asset => !asset.endsWith('html'))
+let dynamicAssets = assets.filter(asset => asset.endsWith('html'))
+ .concat(['./', './CardInfo.json'])
+
+console.group(`Service worker ${hash} starts up.`)
+
+console.group('staticAssets')
+console.log(staticAssets.join('\n'))
+console.groupEnd()
+
+console.group('dynamicAssets')
+console.log(dynamicAssets.join('\n'))
+console.groupEnd()
+
+console.groupEnd()
+
+self.addEventListener('install', event => {
+ console.log('Installing...')
+ // Prefetch
+ event.waitUntil(
+ Promise.all([
+ caches.open('static')
+ .then(cache => cache.addAll(staticAssets)),
+ caches.open(`dynamic ${hash}`)
+ .then(cache => cache.addAll(dynamicAssets))
+ ])
+ .then(() => {
+ console.log('Installed.')
+ })
+ .then(() => {
+ if (self.skipWaiting) {
+ return self.skipWaiting()
+ }
+ })
+ )
+})
+
+self.addEventListener('activate', event => {
+ console.log('Activating...')
+ // Remove old caches
+ event.waitUntil(
+ // Remove outdated dynamic assets
+ caches.keys()
+ .then(keys => {
+ console.group('Delete outdated dynamic cache')
+ return Promise.all(keys.map(key => {
+ if (key.startsWith('dynamic ') && key !== `dynamic ${hash}`) {
+ console.log(key)
+ return caches.delete(key)
+ }
+ }))
+ .then(() => {
+ console.groupEnd()
+ })
+ })
+ .then(() => {
+ // Remove no longer used static assets
+ let absoluteUrls = staticAssets.map(relatedUrl => {
+ return (new URL(relatedUrl, self.location)).href
+ })
+ return caches.open('static').then(cache => {
+ return cache.keys().then(requests => {
+ console.group('Delete unused static assets')
+ return Promise.all(requests.map(request => {
+ if (!absoluteUrls.includes(request.url)) {
+ console.log(request.url)
+ return cache.delete(request)
+ }
+ }))
+ .then(() => {
+ console.groupEnd()
+ })
+ })
+ })
+ })
+ .then(() => {
+ if (self.clients.claim) {
+ return self.clients.claim()
+ }
+ })
+ .then(() => {
+ console.log('Activated.')
+ })
+ )
+})
+
+self.addEventListener('fetch', event => {
+ let request = event.request
+
+ return event.respondWith(
+ caches.match(request)
+ .then(response => {
+ if (response) {
+ console.log(`From cache: ${request.url}`)
+ return response
+ }
+ // console.log(`From network: ${request.url}`)
+ return fetch(request)
+ })
+ )
+})
diff --git a/webpack.config.js b/webpack.config.js
index 73a49f6..60b49fd 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -166,11 +166,33 @@ config.base = {
new webpack.optimize.CommonsChunkPlugin({
name: 'vue',
}),
+ new webpack.optimize.CommonsChunkPlugin({
+ name: 'meta',
+ chunks: ['vue'],
+ }),
new html({
title: 'test',
filename: 'index.html',
template: PATHS.template,
}),
+ new (require('appcache-webpack-plugin'))({
+ cache: ['CardInfo.json'],
+ exclude: ['index.html'],
+ output: 'webxoss.appcache',
+ }),
+ new (require('serviceworker-webpack-plugin'))({
+ entry: './src/service-worker.js',
+ filename: 'service-worker.js',
+ excludes: ['**/*.', '**/*.map', '**/*.appcache', '**/*.hot-update.*'],
+ template: option => {
+ // add hash
+ let hash = require('crypto').createHash('sha256')
+ hash.update(JSON.stringify(option))
+ hash.update(require('fs').readFileSync('./src/service-worker.js'))
+ option.hash = hash.digest('base64').slice(0, 7)
+ return Promise.resolve('')
+ },
+ }),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,