mirror of
https://github.com/moparisthebest/mail
synced 2025-01-30 14:40:13 -05:00
Add simple prefetch service-worker
This commit is contained in:
parent
7c1d68ec6e
commit
504e8ffd50
83
Gruntfile.js
83
Gruntfile.js
@ -535,7 +535,7 @@ module.exports = function(grunt) {
|
|||||||
watch: {
|
watch: {
|
||||||
css: {
|
css: {
|
||||||
files: ['src/sass/**/*.scss'],
|
files: ['src/sass/**/*.scss'],
|
||||||
tasks: ['dist-css', 'manifest', 'dist-styleguide']
|
tasks: ['dist-css', 'offline-cache', 'dist-styleguide']
|
||||||
},
|
},
|
||||||
styleguide: {
|
styleguide: {
|
||||||
files: ['src/styleguide/**/*.hbs', 'src/styleguide/**/*.js'],
|
files: ['src/styleguide/**/*.hbs', 'src/styleguide/**/*.js'],
|
||||||
@ -555,15 +555,15 @@ module.exports = function(grunt) {
|
|||||||
},
|
},
|
||||||
icons: {
|
icons: {
|
||||||
files: ['src/index.html', 'src/img/icons/*.svg', '!src/img/icons/all.svg'],
|
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: {
|
lib: {
|
||||||
files: ['src/lib/**/*.js'],
|
files: ['src/lib/**/*.js'],
|
||||||
tasks: ['copy:lib', 'manifest']
|
tasks: ['copy:lib', 'offline-cache']
|
||||||
},
|
},
|
||||||
app: {
|
app: {
|
||||||
files: ['src/*.js', 'src/*.html', 'src/tpl/**/*.html', 'src/**/*.json', 'src/manifest.*', 'src/img/**/*', 'src/font/**/*'],
|
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) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Offline caching
|
||||||
|
|
||||||
|
swPrecache: {
|
||||||
|
prod: {
|
||||||
|
handleFetch: true,
|
||||||
|
rootDir: 'dist'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
manifest: {
|
manifest: {
|
||||||
generate: {
|
generate: {
|
||||||
options: {
|
options: {
|
||||||
@ -594,6 +603,9 @@ module.exports = function(grunt) {
|
|||||||
'manifest.webapp',
|
'manifest.webapp',
|
||||||
'manifest.mobile.json',
|
'manifest.mobile.json',
|
||||||
'background.js',
|
'background.js',
|
||||||
|
'service-worker.js',
|
||||||
|
'styleguide/css/styleguide.min.css',
|
||||||
|
'styleguide/index.html',
|
||||||
'js/app.templates.js',
|
'js/app.templates.js',
|
||||||
'js/app.js.map',
|
'js/app.js.map',
|
||||||
'js/app.min.js.map',
|
'js/app.min.js.map',
|
||||||
@ -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)
|
// Load the plugin(s)
|
||||||
grunt.loadNpmTasks('grunt-browserify');
|
grunt.loadNpmTasks('grunt-browserify');
|
||||||
grunt.loadNpmTasks('grunt-contrib-concat');
|
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||||
@ -688,7 +753,7 @@ module.exports = function(grunt) {
|
|||||||
'concat:app',
|
'concat:app',
|
||||||
'concat:readSandbox',
|
'concat:readSandbox',
|
||||||
'concat:pbkdf2Worker',
|
'concat:pbkdf2Worker',
|
||||||
'manifest'
|
'offline-cache'
|
||||||
]);
|
]);
|
||||||
grunt.registerTask('dist-js-unitTest', [
|
grunt.registerTask('dist-js-unitTest', [
|
||||||
'browserify:unitTest',
|
'browserify:unitTest',
|
||||||
@ -706,6 +771,8 @@ module.exports = function(grunt) {
|
|||||||
// generate styleguide after manifest to forward version number to styleguide
|
// 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('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
|
// Test/Dev tasks
|
||||||
grunt.registerTask('dev', ['connect:dev']);
|
grunt.registerTask('dev', ['connect:dev']);
|
||||||
grunt.registerTask('test', ['jshint', 'connect:test', 'mocha_phantomjs']);
|
grunt.registerTask('test', ['jshint', 'connect:test', 'mocha_phantomjs']);
|
||||||
@ -770,9 +837,9 @@ module.exports = function(grunt) {
|
|||||||
fs.writeFileSync(path, JSON.stringify(manifest, null, 2));
|
fs.writeFileSync(path, JSON.stringify(manifest, null, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
grunt.registerTask('release-dev', ['dist', 'manifest-dev', 'compress']);
|
grunt.registerTask('release-dev', ['dist', 'manifest-dev', 'swPrecache:prod', 'compress']);
|
||||||
grunt.registerTask('release-test', ['dist', 'manifest-test', 'clean:release', 'compress']);
|
grunt.registerTask('release-test', ['dist', 'manifest-test', 'clean:release', 'swPrecache:prod', 'compress']);
|
||||||
grunt.registerTask('release-prod', ['dist', 'manifest-prod', 'clean:release', 'compress']);
|
grunt.registerTask('release-prod', ['dist', 'manifest-prod', 'clean:release', 'swPrecache:prod', 'compress']);
|
||||||
grunt.registerTask('default', ['release-dev']);
|
grunt.registerTask('default', ['release-dev']);
|
||||||
|
|
||||||
};
|
};
|
@ -34,6 +34,7 @@
|
|||||||
"socket.io": "^1.0.6"
|
"socket.io": "^1.0.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"assemble": "~0.4.42",
|
||||||
"axe-logger": "~0.0.2",
|
"axe-logger": "~0.0.2",
|
||||||
"browsercrow": "https://github.com/whiteout-io/browsercrow/tarball/master",
|
"browsercrow": "https://github.com/whiteout-io/browsercrow/tarball/master",
|
||||||
"browsersmtp": "https://github.com/whiteout-io/browsersmtp/tarball/master",
|
"browsersmtp": "https://github.com/whiteout-io/browsersmtp/tarball/master",
|
||||||
@ -61,6 +62,7 @@
|
|||||||
"grunt-string-replace": "~1.0.0",
|
"grunt-string-replace": "~1.0.0",
|
||||||
"grunt-svgmin": "~1.0.0",
|
"grunt-svgmin": "~1.0.0",
|
||||||
"grunt-svgstore": "~0.3.4",
|
"grunt-svgstore": "~0.3.4",
|
||||||
|
"handlebars-helper-compose": "~0.2.12",
|
||||||
"iframe-resizer": "^2.8.3",
|
"iframe-resizer": "^2.8.3",
|
||||||
"imap-client": "~0.14.1",
|
"imap-client": "~0.14.1",
|
||||||
"jquery": "~2.1.1",
|
"jquery": "~2.1.1",
|
||||||
@ -72,10 +74,9 @@
|
|||||||
"pgpbuilder": "~0.6.0",
|
"pgpbuilder": "~0.6.0",
|
||||||
"pgpmailer": "~0.9.1",
|
"pgpmailer": "~0.9.1",
|
||||||
"sinon": "~1.7.3",
|
"sinon": "~1.7.3",
|
||||||
|
"sw-precache": "^1.3.0",
|
||||||
"tcp-socket": "~0.5.0",
|
"tcp-socket": "~0.5.0",
|
||||||
"time-grunt": "^1.0.0",
|
"time-grunt": "^1.0.0",
|
||||||
"wo-smtpclient": "~0.6.0",
|
"wo-smtpclient": "~0.6.0"
|
||||||
"assemble": "~0.4.42",
|
|
||||||
"handlebars-helper-compose": "~0.2.12"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,10 +88,13 @@ app.use(function(req, res, next) {
|
|||||||
res.set('Cache-control', 'public, max-age=0');
|
res.set('Cache-control', 'public, max-age=0');
|
||||||
next();
|
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');
|
res.set('Cache-control', 'no-cache');
|
||||||
next();
|
next();
|
||||||
});
|
}
|
||||||
app.use('/tpl/read-sandbox.html', function(req, res, next) {
|
app.use('/tpl/read-sandbox.html', function(req, res, next) {
|
||||||
res.set('X-Frame-Options', 'SAMEORIGIN');
|
res.set('X-Frame-Options', 'SAMEORIGIN');
|
||||||
next();
|
next();
|
||||||
|
@ -1,22 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
//
|
// use service-worker or app-cache for offline caching
|
||||||
// AppCache
|
require('./offline-cache');
|
||||||
//
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Angular app config
|
// Angular app config
|
||||||
|
98
src/js/offline-cache.js
Normal file
98
src/js/offline-cache.js
Normal file
@ -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…
Reference in New Issue
Block a user