Browse Source

Add simple prefetch service-worker

master
Tankred Hase 8 years ago
parent
commit
504e8ffd50
  1. 83
      Gruntfile.js
  2. 9
      package.json
  3. 7
      server.js
  4. 19
      src/js/app.js
  5. 98
      src/js/offline-cache.js

83
Gruntfile.js

@ -535,7 +535,7 @@ module.exports = function(grunt) { @@ -535,7 +535,7 @@ module.exports = function(grunt) {
watch: {
css: {
files: ['src/sass/**/*.scss'],
tasks: ['dist-css', 'manifest', 'dist-styleguide']
tasks: ['dist-css', 'offline-cache', 'dist-styleguide']
},
styleguide: {
files: ['src/styleguide/**/*.hbs', 'src/styleguide/**/*.js'],
@ -555,15 +555,15 @@ module.exports = function(grunt) { @@ -555,15 +555,15 @@ module.exports = function(grunt) {
},
icons: {
files: ['src/index.html', 'src/img/icons/*.svg', '!src/img/icons/all.svg'],
tasks: ['svgmin', 'svgstore', 'string-replace', 'dist-styleguide', 'manifest']
tasks: ['svgmin', 'svgstore', 'string-replace', 'dist-styleguide', 'offline-cache']
},
lib: {
files: ['src/lib/**/*.js'],
tasks: ['copy:lib', 'manifest']
tasks: ['copy:lib', 'offline-cache']
},
app: {
files: ['src/*.js', 'src/*.html', 'src/tpl/**/*.html', 'src/**/*.json', 'src/manifest.*', 'src/img/**/*', 'src/font/**/*'],
tasks: ['copy:app', 'copy:tpl', 'copy:img', 'copy:font', 'manifest-dev', 'manifest']
tasks: ['copy:app', 'copy:tpl', 'copy:img', 'copy:font', 'manifest-dev', 'offline-cache']
}
},
@ -582,6 +582,15 @@ module.exports = function(grunt) { @@ -582,6 +582,15 @@ module.exports = function(grunt) {
}
},
// Offline caching
swPrecache: {
prod: {
handleFetch: true,
rootDir: 'dist'
}
},
manifest: {
generate: {
options: {
@ -594,6 +603,9 @@ module.exports = function(grunt) { @@ -594,6 +603,9 @@ module.exports = function(grunt) {
'manifest.webapp',
'manifest.mobile.json',
'background.js',
'service-worker.js',
'styleguide/css/styleguide.min.css',
'styleguide/index.html',
'js/app.templates.js',
'js/app.js.map',
'js/app.min.js.map',
@ -654,6 +666,59 @@ module.exports = function(grunt) { @@ -654,6 +666,59 @@ module.exports = function(grunt) {
});
// generate service-worker stasks
grunt.registerMultiTask('swPrecache', function() {
var fs = require('fs');
var path = require('path');
var swPrecache = require('sw-precache');
var packageJson = require('./package.json');
var done = this.async();
var rootDir = this.data.rootDir;
var handleFetch = this.data.handleFetch;
generateServiceWorkerFileContents(rootDir, handleFetch, function(error, serviceWorkerFileContents) {
if (error) {
grunt.fail.warn(error);
}
fs.writeFile(path.join(rootDir, 'service-worker.js'), serviceWorkerFileContents, function(error) {
if (error) {
grunt.fail.warn(error);
}
done();
});
});
function generateServiceWorkerFileContents(rootDir, handleFetch, callback) {
var config = {
cacheId: packageJson.name,
// If handleFetch is false (i.e. because this is called from swPrecache:dev), then
// the service worker will precache resources but won't actually serve them.
// This allows you to test precaching behavior without worry about the cache preventing your
// local changes from being picked up during the development cycle.
handleFetch: handleFetch,
logger: grunt.log.writeln,
dynamicUrlToDependencies: {
'socket.io/socket.io.js': ['node_modules/socket.io/node_modules/socket.io-client/socket.io.js'],
},
staticFileGlobs: [
rootDir + '/*.html',
rootDir + '/tpl/*.html',
rootDir + '/js/**/*.min.js',
rootDir + '/css/**/*.css',
rootDir + '/img/**/*.svg',
rootDir + '/img/*-universal.png',
rootDir + '/font/**.*',
rootDir + '/*.json'
],
maximumFileSizeToCacheInBytes: 100 * 1024 * 1024,
stripPrefix: path.join(rootDir, path.sep)
};
swPrecache(config, callback);
}
});
// Load the plugin(s)
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-contrib-concat');
@ -688,7 +753,7 @@ module.exports = function(grunt) { @@ -688,7 +753,7 @@ module.exports = function(grunt) {
'concat:app',
'concat:readSandbox',
'concat:pbkdf2Worker',
'manifest'
'offline-cache'
]);
grunt.registerTask('dist-js-unitTest', [
'browserify:unitTest',
@ -706,6 +771,8 @@ module.exports = function(grunt) { @@ -706,6 +771,8 @@ module.exports = function(grunt) {
// generate styleguide after manifest to forward version number to styleguide
grunt.registerTask('dist', ['clean:dist', 'shell', 'dist-css', 'dist-js', 'dist-assets', 'dist-copy', 'manifest', 'dist-styleguide']);
grunt.registerTask('offline-cache', ['manifest', 'swPrecache:prod']);
// Test/Dev tasks
grunt.registerTask('dev', ['connect:dev']);
grunt.registerTask('test', ['jshint', 'connect:test', 'mocha_phantomjs']);
@ -770,9 +837,9 @@ module.exports = function(grunt) { @@ -770,9 +837,9 @@ module.exports = function(grunt) {
fs.writeFileSync(path, JSON.stringify(manifest, null, 2));
}
grunt.registerTask('release-dev', ['dist', 'manifest-dev', 'compress']);
grunt.registerTask('release-test', ['dist', 'manifest-test', 'clean:release', 'compress']);
grunt.registerTask('release-prod', ['dist', 'manifest-prod', 'clean:release', 'compress']);
grunt.registerTask('release-dev', ['dist', 'manifest-dev', 'swPrecache:prod', 'compress']);
grunt.registerTask('release-test', ['dist', 'manifest-test', 'clean:release', 'swPrecache:prod', 'compress']);
grunt.registerTask('release-prod', ['dist', 'manifest-prod', 'clean:release', 'swPrecache:prod', 'compress']);
grunt.registerTask('default', ['release-dev']);
};

9
package.json

@ -34,6 +34,7 @@ @@ -34,6 +34,7 @@
"socket.io": "^1.0.6"
},
"devDependencies": {
"assemble": "~0.4.42",
"axe-logger": "~0.0.2",
"browsercrow": "https://github.com/whiteout-io/browsercrow/tarball/master",
"browsersmtp": "https://github.com/whiteout-io/browsersmtp/tarball/master",
@ -61,6 +62,7 @@ @@ -61,6 +62,7 @@
"grunt-string-replace": "~1.0.0",
"grunt-svgmin": "~1.0.0",
"grunt-svgstore": "~0.3.4",
"handlebars-helper-compose": "~0.2.12",
"iframe-resizer": "^2.8.3",
"imap-client": "~0.14.1",
"jquery": "~2.1.1",
@ -72,10 +74,9 @@ @@ -72,10 +74,9 @@
"pgpbuilder": "~0.6.0",
"pgpmailer": "~0.9.1",
"sinon": "~1.7.3",
"sw-precache": "^1.3.0",
"tcp-socket": "~0.5.0",
"time-grunt": "^1.0.0",
"wo-smtpclient": "~0.6.0",
"assemble": "~0.4.42",
"handlebars-helper-compose": "~0.2.12"
"wo-smtpclient": "~0.6.0"
}
}
}

7
server.js

@ -88,10 +88,13 @@ app.use(function(req, res, next) { @@ -88,10 +88,13 @@ app.use(function(req, res, next) {
res.set('Cache-control', 'public, max-age=0');
next();
});
app.use('/appcache.manifest', function(req, res, next) {
app.use('/service-worker.js', noCache);
app.use('/appcache.manifest', noCache);
function noCache(req, res, next) {
res.set('Cache-control', 'no-cache');
next();
});
}
app.use('/tpl/read-sandbox.html', function(req, res, next) {
res.set('X-Frame-Options', 'SAMEORIGIN');
next();

19
src/js/app.js

@ -1,22 +1,7 @@ @@ -1,22 +1,7 @@
'use strict';
//
// AppCache
//
if (typeof window.applicationCache !== 'undefined') {
window.onload = function() {
// Check if a new AppCache is available on page load.
window.applicationCache.onupdateready = function() {
if (window.applicationCache.status === window.applicationCache.UPDATEREADY) {
// Browser downloaded a new app cache
if (window.confirm('A new version of Whiteout Mail is available. Restart the app to update?')) {
window.location.reload();
}
}
};
};
}
// use service-worker or app-cache for offline caching
require('./offline-cache');
//
// Angular app config

98
src/js/offline-cache.js

@ -0,0 +1,98 @@ @@ -0,0 +1,98 @@
/**
* Copyright 2015 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
var UPDATE_MSG = 'A new version of Whiteout Mail is available. Restart the app to update?';
if ('serviceWorker' in navigator &&
// See http://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features
(window.location.protocol === 'https:' ||
window.location.hostname === 'localhost' ||
window.location.hostname.indexOf('127.') === 0)) {
// prefer new service worker cache
useServiceWorker();
} else if ('applicationCache' in window) {
// Fall back to app cache
useAppCache();
}
function useServiceWorker() {
// Your service-worker.js *must* be located at the top-level directory relative to your site.
// It won't be able to control pages unless it's located at the same level or higher than them.
// *Don't* register service worker file in, e.g., a scripts/ sub-directory!
// See https://github.com/slightlyoff/ServiceWorker/issues/468
navigator.serviceWorker.register('service-worker.js', {
scope: './'
}).then(function(registration) {
// Check to see if there's an updated version of service-worker.js with new files to cache:
// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-registration-update-method
if (typeof registration.update === 'function') {
registration.update();
}
// updatefound is fired if service-worker.js changes.
registration.onupdatefound = function() {
// The updatefound event implies that registration.installing is set; see
// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event
var installingWorker = registration.installing;
installingWorker.onstatechange = function() {
switch (installingWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and the fresh content will
// have been added to the cache.
// It's the perfect time to display a "New content is available; please refresh."
// message in the page's interface.
console.log('New or updated content is available.');
if (window.confirm(UPDATE_MSG)) {
window.location.reload();
}
} else {
// At this point, everything has been precached, but the service worker is not
// controlling the page. The service worker will not take control until the next
// reload or navigation to a page under the registered scope.
// It's the perfect time to display a "Content is cached for offline use." message.
console.log('Content is cached, and will be available for offline use the ' +
'next time the page is loaded.');
}
break;
case 'redundant':
throw 'The installing service worker became redundant.';
}
};
};
}).catch(function(e) {
console.error('Error during service worker registration:', e);
});
}
function useAppCache() {
window.onload = function() {
// Check if a new AppCache is available on page load.
window.applicationCache.onupdateready = function() {
if (window.applicationCache.status === window.applicationCache.UPDATEREADY) {
// Browser downloaded a new app cache
if (window.confirm(UPDATE_MSG)) {
window.location.reload();
}
}
};
};
}
Loading…
Cancel
Save