Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion build.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion src/components/DeckFloatButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export default {
<style module>
@import 'css/vars.css';
.button {
position: fixed;
position: absolute;
bottom: 2rem;
right: 2rem;
width: 4rem;
Expand Down
82 changes: 82 additions & 0 deletions src/components/UpdateStatusBar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<script>
let state = {
// ['', 'updating', 'ready']
status: '',
total: 0,
loaded: 0,
}
let cache = window.applicationCache
if (cache) {
cache.addEventListener('downloading', () => {
state.status = 'updating'
}, false)
cache.addEventListener('updateready', () => {
state.status = 'ready'
}, false)
cache.addEventListener('cached', () => {
state.status = 'ready'
}, false)
cache.addEventListener('progress', () => {
state.total = event.total
state.loaded = event.loaded
}, false)
}

export default {
data: () => state,
computed: {
percent() {
if (!this.total) {
return ''
}
return `${Math.round(this.loaded / this.total * 100)}%`
},
text() {
return {
updating: `Updating... ${this.percent}`,
ready: 'A new version is ready',
}[this.status] || ''
},
},
methods: {
reload() {
location.reload()
},
},
}
</script>

<template>
<div :class="[$style.bar, status ? '' : $style.hidden]">
<span :class="$style.text">{{ text }}</span>
<button
:class="$style.button"
@click="reload"
v-if="status === 'ready'">
SWITCH
</button>
</div>
</template>

<style module>
.bar {
position: relative;
color: #fff;
background-color: #333;
padding: 0 2em;
height: 3.5em;

display: flex;
align-items: center;
justify-content: space-between;

transition: height .2s ease-out;
&.hidden {
height: 0;
}
}
.button {
color: #4caf50;
font-weight: bold;
}
</style>
5 changes: 5 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,8 @@ $get('./CardInfo.json')
})
}, 1000)
})

import runtime from 'serviceworker-webpack-plugin/lib/runtime'
if ('serviceWorker' in navigator) {
runtime.register()
}
2 changes: 1 addition & 1 deletion src/index.tpl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html manifest="webxoss.appcache">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
Expand Down
32 changes: 30 additions & 2 deletions src/pages/Deck.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -20,6 +21,7 @@ export default {
HeaderMenu,
DeckModals,
DeckFloatButton,
UpdateStatusBar,
Cell,
Block,
DeckHead,
Expand Down Expand Up @@ -203,9 +205,25 @@ export default {
</div>
</div>

<deck-float-button />
<!-- Bottom -->
<!--
Bottom layer is fixed to screen bottom.
Its `div` children are `position: relative`.
So, floating buttons won't get blocked.
-->
<div :class="$style.bottom">
<div>
<deck-float-button/>
</div>
<div>
<update-status-bar/>
</div>
</div>

<!-- Full screen wrappers -->
<header-menu ref="menu" :items="menuItems"/>
<deck-modals ref="modals"/>

</div>
</template>

Expand All @@ -225,4 +243,14 @@ export default {
.block {
width: 20%;
}
</style>
.bottom {
position: fixed;
left: 0;
right: 0;
bottom: 0;

& > div {
position: relative;
}
}
</style>
105 changes: 105 additions & 0 deletions src/service-worker.js
Original file line number Diff line number Diff line change
@@ -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)
})
)
})
22 changes: 22 additions & 0 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down