Added A2HS prompt

This commit is contained in:
Benoit Schweblin 2018-09-21 10:12:05 +01:00
parent f7542965b6
commit 0e8aa0a58a
8 changed files with 208 additions and 19 deletions

View File

@ -5,7 +5,6 @@ var config = require('../config')
var VueLoaderPlugin = require('vue-loader/lib/plugin')
var vueLoaderConfig = require('./vue-loader.conf')
var StylelintPlugin = require('stylelint-webpack-plugin')
var FaviconsWebpackPlugin = require('favicons-webpack-plugin')
function resolve (dir) {
return path.join(__dirname, '..', dir)
@ -48,16 +47,33 @@ module.exports = {
loader: 'vue-loader',
options: vueLoaderConfig
},
// We can't pass graphlibrary to babel
{
test: /\.js$/,
loader: 'string-replace-loader',
include: [
resolve('node_modules/graphlibrary')
],
options: {
search: '^\\s*(?:let|const) ',
replace: 'var ',
flags: 'gm'
}
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test'), resolve('node_modules/mermaid/src')],
include: [
resolve('src'),
resolve('test'),
resolve('node_modules/mermaid')
],
exclude: [
resolve('node_modules/mermaid/src/diagrams/class/parser'),
resolve('node_modules/mermaid/src/diagrams/flowchart/parser'),
resolve('node_modules/mermaid/src/diagrams/gantt/parser'),
resolve('node_modules/mermaid/src/diagrams/git/parser'),
resolve('node_modules/mermaid/src/diagrams/sequence/parser'),
resolve('node_modules/mermaid/src/diagrams/sequence/parser')
],
},
{
@ -86,10 +102,6 @@ module.exports = {
new StylelintPlugin({
files: ['**/*.vue', '**/*.scss']
}),
new FaviconsWebpackPlugin({
logo: resolve('src/assets/favicon.png'),
title: 'StackEdit',
}),
new webpack.DefinePlugin({
VERSION: JSON.stringify(require('../package.json').version)
})

View File

@ -9,6 +9,12 @@ var HtmlWebpackPlugin = require('html-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
var OfflinePlugin = require('offline-plugin');
var WebpackPwaManifest = require('webpack-pwa-manifest')
var FaviconsWebpackPlugin = require('favicons-webpack-plugin')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
var env = config.build.env
@ -94,6 +100,22 @@ var webpackConfig = merge(baseWebpackConfig, {
ignore: ['.*']
}
]),
new FaviconsWebpackPlugin({
logo: resolve('src/assets/favicon.png'),
title: 'StackEdit',
}),
new WebpackPwaManifest({
name: 'StackEdit',
description: 'Full-featured, open-source Markdown editor',
display: 'standalone',
start_url: 'app',
background_color: '#ffffff',
crossorigin: 'use-credentials',
icons: [{
src: resolve('src/assets/favicon.png'),
sizes: [96, 128, 192, 256, 384, 512]
}]
}),
new OfflinePlugin({
ServiceWorker: {
events: true
@ -101,7 +123,7 @@ var webpackConfig = merge(baseWebpackConfig, {
AppCache: true,
excludes: ['**/.*', '**/*.map', '**/index.html', '**/static/oauth2/callback.html', '**/icons-*/*.png', '**/static/fonts/KaTeX_*'],
externals: ['/', '/app', '/oauth2/callback']
})
}),
]
})

82
package-lock.json generated
View File

@ -16854,6 +16854,69 @@
}
}
},
"string-replace-loader": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-replace-loader/-/string-replace-loader-2.1.1.tgz",
"integrity": "sha512-0Nvw1LDclF45AFNuYPcD2Jvkv0mwb/dQSnJZMvhqGrT+zzmrpG3OJFD600qfQfNUd5aqfp7fCm2mQMfF7zLbyQ==",
"dev": true,
"requires": {
"loader-utils": "^1.1.0",
"schema-utils": "^0.4.5"
},
"dependencies": {
"ajv": {
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz",
"integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==",
"dev": true,
"requires": {
"fast-deep-equal": "^2.0.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"ajv-keywords": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz",
"integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=",
"dev": true
},
"fast-deep-equal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
"dev": true
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"loader-utils": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz",
"integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=",
"dev": true,
"requires": {
"big.js": "^3.1.3",
"emojis-list": "^2.0.0",
"json5": "^0.5.0"
}
},
"schema-utils": {
"version": "0.4.7",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz",
"integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==",
"dev": true,
"requires": {
"ajv": "^6.1.0",
"ajv-keywords": "^3.1.0"
}
}
}
},
"string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
@ -19774,6 +19837,25 @@
}
}
},
"webpack-pwa-manifest": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/webpack-pwa-manifest/-/webpack-pwa-manifest-3.7.1.tgz",
"integrity": "sha512-G37fVCa1ndij3jyz6WaOaxHLHdp2URyOHwp2GLmxt39sXL8ZdOFM1qvHagEJBkNh+3hu25eIgy6TD5J/8sgQcQ==",
"dev": true,
"requires": {
"css-color-names": "0.0.4",
"jimp": "^0.2.28",
"mime": "^1.6.0"
},
"dependencies": {
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"dev": true
}
}
},
"webpack-sources": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz",

View File

@ -110,6 +110,7 @@
"sass-loader": "^7.0.1",
"semver": "^5.5.0",
"shelljs": "^0.8.1",
"string-replace-loader": "^2.1.1",
"stylelint": "^9.2.0",
"stylelint-config-standard": "^16.0.0",
"stylelint-processor-html": "^1.0.0",
@ -124,6 +125,7 @@
"webpack-dev-middleware": "^1.10.0",
"webpack-hot-middleware": "^2.18.0",
"webpack-merge": "^4.1.2",
"webpack-pwa-manifest": "^3.7.1",
"worker-loader": "^1.1.1"
},
"engines": {

View File

@ -2,12 +2,18 @@
<div class="notification">
<div class="notification__item flex flex--row flex--align-center" v-for="(item, idx) in items" :key="idx">
<div class="notification__icon flex flex--column flex--center">
<icon-information v-if="item.type === 'info'"></icon-information>
<icon-alert v-else-if="item.type === 'error'"></icon-alert>
<icon-alert v-if="item.type === 'error'"></icon-alert>
<icon-information v-else></icon-information>
</div>
<div class="notification__content">
{{item.content}}
</div>
<button class="notification__button button" v-if="item.type === 'confirm'" @click="item.reject">
No
</button>
<button class="notification__button button" v-if="item.type === 'confirm'" @click="item.resolve">
Yes
</button>
</div>
</div>
</template>
@ -49,4 +55,17 @@ export default {
margin-right: 12px;
flex: none;
}
.notification__button {
color: $navbar-color;
padding: 8px;
flex: none;
&:active,
&:focus,
&:hover {
color: $navbar-hover-color;
background-color: $navbar-hover-background;
}
}
</style>

View File

@ -33,6 +33,22 @@ if (localStorage.updated) {
setTimeout(() => localStorage.removeItem('updated'), 2000);
}
if (!localStorage.installPrompted) {
window.addEventListener('beforeinstallprompt', async (promptEvent) => {
// Prevent Chrome 67 and earlier from automatically showing the prompt
promptEvent.preventDefault();
try {
await store.dispatch('notification/confirm', 'Add StackEdit to your home screen?');
promptEvent.prompt();
await promptEvent.userChoice;
} catch (err) {
// Cancel
}
localStorage.installPrompted = true;
});
}
Vue.config.productionTip = false;
/* eslint-disable no-new */

View File

@ -7,6 +7,7 @@ const clientId = GOOGLE_CLIENT_ID;
const apiKey = 'AIzaSyC_M4RA9pY6XmM9pmFxlT59UPMO7aHr9kk';
const appsDomain = null;
const tokenExpirationMargin = 5 * 60 * 1000; // 5 min (Google tokens expire after 1h)
let googlePlusNotification = true;
const driveAppDataScopes = ['https://www.googleapis.com/auth/drive.appdata'];
const getDriveScopes = token => [token.driveFullAccess
@ -160,7 +161,12 @@ export default {
// Call the user info endpoint
const user = await getUser('me', token);
token.name = user.displayName;
if (user.displayName) {
token.name = user.displayName;
} else if (googlePlusNotification) {
store.dispatch('notification/info', 'Please activate Google Plus to change your account name and photo.');
googlePlusNotification = false;
}
userSvc.addInfo({
id: `${subPrefix}:${user.id}`,
name: user.displayName,
@ -449,10 +455,10 @@ export default {
},
});
revisions.forEach((revision) => {
store.commit('userInfo/addItem', {
userSvc.addInfo({
id: `${subPrefix}:${revision.lastModifyingUser.permissionId}`,
name: revision.lastModifyingUser.displayName,
imageUrl: revision.lastModifyingUser.photoLink,
imageUrl: revision.lastModifyingUser.photoLink || '',
});
allRevisions.push(revision);
});

View File

@ -1,6 +1,7 @@
import providerRegistry from '../services/providers/common/providerRegistry';
import utils from '../services/utils';
const defaultTimeout = 5000;
const defaultTimeout = 5000; // 5 sec
export default {
namespaced: true,
@ -14,20 +15,49 @@ export default {
},
actions: {
showItem({ state, commit }, item) {
if (state.items.every(other => other.type !== item.type || other.content !== item.content)) {
const existingItem = utils.someResult(
state.items,
other => other.type === item.type && other.content === item.content && item,
);
if (existingItem) {
return existingItem.promise;
}
item.promise = new Promise((resolve, reject) => {
commit('setItems', [...state.items, item]);
const removeItem = () => commit(
'setItems',
state.items.filter(otherItem => otherItem !== item),
);
setTimeout(
() => commit('setItems', state.items.filter(otherItem => otherItem !== item)),
() => removeItem(),
item.timeout || defaultTimeout,
);
}
item.resolve = (res) => {
removeItem();
resolve(res);
};
item.reject = (err) => {
removeItem();
reject(err);
};
});
return item.promise;
},
info({ dispatch }, info) {
dispatch('showItem', {
return dispatch('showItem', {
type: 'info',
content: info,
});
},
confirm({ dispatch }, question) {
return dispatch('showItem', {
type: 'confirm',
content: question,
timeout: 10000, // 10 sec
});
},
error({ dispatch, rootState }, error) {
const item = { type: 'error' };
if (error) {
@ -48,7 +78,7 @@ export default {
if (!item.content || item.content === '[object Object]') {
item.content = 'Unknown error.';
}
dispatch('showItem', item);
return dispatch('showItem', item);
},
},
};