Added undo/redo buttons. Restore file when imported from Google Drive and Dropbox. Lint server

This commit is contained in:
benweet 2017-10-14 12:39:15 +01:00
parent e15b9fae16
commit 83a94dbdff
22 changed files with 147 additions and 226 deletions

View File

@ -12,7 +12,7 @@
"postinstall": "gulp build-prism", "postinstall": "gulp build-prism",
"start": "node build/dev-server.js", "start": "node build/dev-server.js",
"build": "node build/build.js", "build": "node build/build.js",
"lint": "eslint --ext .js,.vue src", "lint": "eslint --ext .js,.vue src server",
"preversion": "npm run lint", "preversion": "npm run lint",
"postversion": "git push origin master --tags && npm publish", "postversion": "git push origin master --tags && npm publish",
"patch": "npm version patch -m \"Tag v%s\"", "patch": "npm version patch -m \"Tag v%s\"",

View File

@ -1,31 +1,35 @@
var compression = require('compression'); const compression = require('compression');
var serveStatic = require('serve-static'); const serveStatic = require('serve-static');
var path = require('path'); const path = require('path');
const github = require('./github');
module.exports = function (app, serveV4) { module.exports = (app, serveV4) => {
// Use gzip compression // Use gzip compression
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
app.all('*', function(req, res, next) { app.all('*', (req, res, next) => {
// Force HTTPS on stackedit.io // Force HTTPS on stackedit.io
if (req.headers.host === 'stackedit.io' && !req.secure && req.headers['x-forwarded-proto'] !== 'https') { if (req.headers.host === 'stackedit.io' && !req.secure && req.headers['x-forwarded-proto'] !== 'https') {
return res.redirect('https://stackedit.io' + req.url); res.redirect(`https://stackedit.io${req.url}`);
return;
} }
// Enable CORS for fonts // Enable CORS for fonts
if (/\.(eot|ttf|woff|svg)$/.test(req.url)) { if (/\.(eot|ttf|woff|svg)$/.test(req.url)) {
res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Origin', '*');
} };
next(); next();
}); });
app.use(compression()); app.use(compression());
} }
app.get('/oauth2/githubToken', require('./github').githubToken); app.get('/oauth2/githubToken', github.githubToken);
if (serveV4) { if (serveV4) {
/* eslint-disable global-require, import/no-unresolved */
app.post('/pdfExport', require('../stackedit_v4/app/pdf').export); app.post('/pdfExport', require('../stackedit_v4/app/pdf').export);
app.post('/sshPublish', require('../stackedit_v4/app/ssh').publish); app.post('/sshPublish', require('../stackedit_v4/app/ssh').publish);
app.post('/picasaImportImg', require('../stackedit_v4/app/picasa').importImg); app.post('/picasaImportImg', require('../stackedit_v4/app/picasa').importImg);
app.get('/downloadImport', require('../stackedit_v4/app/download').importPublic); app.get('/downloadImport', require('../stackedit_v4/app/download').importPublic);
/* eslint-enable global-require, import/no-unresolved */
} }
// Serve callback.html in /app // Serve callback.html in /app
@ -37,23 +41,15 @@ module.exports = function (app, serveV4) {
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
if (serveV4) { if (serveV4) {
// Serve landing.html in / // Serve landing.html in /
app.get('/', function(req, res) { app.get('/', (req, res) => res.sendFile(require.resolve('../stackedit_v4/views/landing.html')));
res.sendFile(require.resolve('../stackedit_v4/views/landing.html'));
});
// Serve editor.html in /viewer // Serve editor.html in /viewer
app.get('/editor', function(req, res) { app.get('/editor', (req, res) => res.sendFile(require.resolve('../stackedit_v4/views/editor.html')));
res.sendFile(require.resolve('../stackedit_v4/views/editor.html'));
});
// Serve viewer.html in /viewer // Serve viewer.html in /viewer
app.get('/viewer', function(req, res) { app.get('/viewer', (req, res) => res.sendFile(require.resolve('../stackedit_v4/views/viewer.html')));
res.sendFile(require.resolve('../stackedit_v4/views/viewer.html'));
});
} }
// Serve index.html in /app // Serve index.html in /app
app.get('/app', function(req, res) { app.get('/app', (req, res) => res.sendFile(path.join(__dirname, '../dist/index.html')));
res.sendFile(path.join(__dirname, '../dist/index.html'));
});
app.use(serveStatic(path.join(__dirname, '../dist'))); app.use(serveStatic(path.join(__dirname, '../dist')));
@ -61,9 +57,7 @@ module.exports = function (app, serveV4) {
app.use(serveStatic(path.dirname(require.resolve('../stackedit_v4/public/cache.manifest')))); app.use(serveStatic(path.dirname(require.resolve('../stackedit_v4/public/cache.manifest'))));
// Error 404 // Error 404
app.use(function(req, res) { app.use((req, res) => res.status(404).sendFile(require.resolve('../stackedit_v4/views/error_404.html')));
res.status(404).sendFile(require.resolve('../stackedit_v4/views/error_404.html'));
});
} }
} }
}; };

View File

@ -1,145 +0,0 @@
/* global window,MathJax */
var spawn = require('child_process').spawn;
var fs = require('fs');
var path = require('path');
var os = require('os');
var request = require('request');
function waitForJavaScript() {
if(window.MathJax) {
// Amazon EC2: fix TeX font detection
MathJax.Hub.Register.StartupHook("HTML-CSS Jax Startup",function () {
var HTMLCSS = MathJax.OutputJax["HTML-CSS"];
HTMLCSS.Font.checkWebFont = function (check,font,callback) {
if (check.time(callback)) {
return;
}
if (check.total === 0) {
HTMLCSS.Font.testFont(font);
setTimeout(check,200);
} else {
callback(check.STATUS.OK);
}
};
});
MathJax.Hub.Queue(function () {
window.status = 'done';
});
}
else {
setTimeout(function() {
window.status = 'done';
}, 2000);
}
}
var authorizedPageSizes = [
'A3',
'A4',
'Legal',
'Letter'
];
exports.export = function(req, res, next) {
function onError(err) {
next(err);
}
function onUnknownError() {
res.statusCode = 400;
res.end('Unknown error');
}
function onUnauthorizedError() {
res.statusCode = 401;
res.end('Unauthorized');
}
function onTimeout() {
res.statusCode = 408;
res.end('Request timeout');
}
request({
uri: 'https://monetizejs.com/api/payments',
qs: {
access_token: req.query.token
},
json: true
}, function (err, paymentsRes, payments) {
var authorized = payments && payments.app == 'ESTHdCYOi18iLhhO' && (
(payments.chargeOption && payments.chargeOption.alias == 'once') ||
(payments.subscriptionOption && payments.subscriptionOption.alias == 'yearly'));
if(err || paymentsRes.statusCode != 200 || !authorized) {
return onUnauthorizedError();
}
var options, params = [];
try {
options = JSON.parse(req.query.options);
}
catch(e) {
options = {};
}
// Margins
var marginTop = parseInt(options.marginTop);
params.push('-T', isNaN(marginTop) ? 25 : marginTop);
var marginRight = parseInt(options.marginRight);
params.push('-R', isNaN(marginRight) ? 25 : marginRight);
var marginBottom = parseInt(options.marginBottom);
params.push('-B', isNaN(marginBottom) ? 25 : marginBottom);
var marginLeft = parseInt(options.marginLeft);
params.push('-L', isNaN(marginLeft) ? 25 : marginLeft);
// Header
options.headerCenter && params.push('--header-center', options.headerCenter);
options.headerLeft && params.push('--header-left', options.headerLeft);
options.headerRight && params.push('--header-right', options.headerRight);
options.headerFontName && params.push('--header-font-name', options.headerFontName);
options.headerFontSize && params.push('--header-font-size', options.headerFontSize);
// Footer
options.footerCenter && params.push('--footer-center', options.footerCenter);
options.footerLeft && params.push('--footer-left', options.footerLeft);
options.footerRight && params.push('--footer-right', options.footerRight);
options.footerFontName && params.push('--footer-font-name', options.footerFontName);
options.footerFontSize && params.push('--footer-font-size', options.footerFontSize);
// Page size
params.push('--page-size', authorizedPageSizes.indexOf(options.pageSize) === -1 ? 'A4' : options.pageSize);
// Use a temp file as wkhtmltopdf can't access /dev/stdout on Amazon EC2 for some reason
var filePath = path.join(os.tmpDir(), Date.now() + '.pdf');
var binPath = process.env.WKHTMLTOPDF_PATH || 'wkhtmltopdf';
params.push('--run-script', waitForJavaScript.toString() + 'waitForJavaScript()');
params.push('--window-status', 'done');
var wkhtmltopdf = spawn(binPath, params.concat('-', filePath), {
stdio: [
'pipe',
'ignore',
'ignore'
]
});
var timeoutId = setTimeout(function() {
timeoutId = undefined;
wkhtmltopdf.kill();
}, 30000);
wkhtmltopdf.on('error', onError);
wkhtmltopdf.stdin.on('error', onError);
wkhtmltopdf.on('close', function(code) {
if(!timeoutId) {
return onTimeout();
}
clearTimeout(timeoutId);
if(code) {
return onUnknownError();
}
var readStream = fs.createReadStream(filePath);
readStream.on('open', function() {
readStream.pipe(res);
});
readStream.on('close', function() {
fs.unlink(filePath, function() {
});
});
readStream.on('error', onUnknownError);
});
req.pipe(wkhtmltopdf.stdin);
});
};

View File

@ -160,7 +160,7 @@ export default {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: rgba(180, 180, 180, 0.75); background-color: rgba(128, 128, 128, 0.5);
overflow: auto; overflow: auto;
} }

View File

@ -34,6 +34,13 @@
</div> </div>
</div> </div>
<div class="navigation-bar__inner navigation-bar__inner--edit-buttons"> <div class="navigation-bar__inner navigation-bar__inner--edit-buttons">
<button class="navigation-bar__button button" @click="undo" v-title="'Undo'" :disabled="!canUndo">
<icon-undo></icon-undo>
</button>
<button class="navigation-bar__button button" @click="redo" v-title="'Redo'" :disabled="!canRedo">
<icon-redo></icon-redo>
</button>
<div class="navigation-bar__spacer"></div>
<button class="navigation-bar__button button" @click="pagedownClick('bold')" v-title="'Bold'"> <button class="navigation-bar__button button" @click="pagedownClick('bold')" v-title="'Bold'">
<icon-format-bold></icon-format-bold> <icon-format-bold></icon-format-bold>
</button> </button>
@ -77,6 +84,7 @@
<script> <script>
import { mapState, mapGetters, mapActions } from 'vuex'; import { mapState, mapGetters, mapActions } from 'vuex';
import editorSvc from '../services/editorSvc'; import editorSvc from '../services/editorSvc';
import editorEngineSvc from '../services/editorEngineSvc';
import syncSvc from '../services/syncSvc'; import syncSvc from '../services/syncSvc';
import publishSvc from '../services/publishSvc'; import publishSvc from '../services/publishSvc';
import animationSvc from '../services/animationSvc'; import animationSvc from '../services/animationSvc';
@ -98,6 +106,10 @@ export default {
'isPublishRequested', 'isPublishRequested',
'currentLocation', 'currentLocation',
]), ]),
...mapState('layout', [
'canUndo',
'canRedo',
]),
...mapGetters('layout', [ ...mapGetters('layout', [
'styles', 'styles',
]), ]),
@ -148,6 +160,12 @@ export default {
'toggleExplorer', 'toggleExplorer',
'toggleSideBar', 'toggleSideBar',
]), ]),
undo() {
return editorEngineSvc.clEditor.undoMgr.undo();
},
redo() {
return editorEngineSvc.clEditor.undoMgr.redo();
},
requestSync() { requestSync() {
if (this.isSyncPossible && !this.isSyncRequested) { if (this.isSyncPossible && !this.isSyncRequested) {
syncSvc.requestSync(); syncSvc.requestSync();
@ -234,7 +252,8 @@ export default {
.navigation-bar__inner--edit-buttons { .navigation-bar__inner--edit-buttons {
margin-left: 15px; margin-left: 15px;
.navigation-bar__button { .navigation-bar__button,
.navigation-bar__spacer {
float: left; float: left;
} }
} }
@ -243,13 +262,20 @@ export default {
flex: none; flex: none;
} }
.navigation-bar__button { $button-size: 36px;
width: 36px;
height: 36px; .navigation-bar__button,
padding: 0 8px; .navigation-bar__spacer {
height: $button-size;
padding: 0 4px;
/* prevent from seeing wrapped buttons */ /* prevent from seeing wrapped buttons */
margin-bottom: 20px; margin-bottom: 20px;
}
.navigation-bar__button {
width: $button-size;
padding: 0 8px;
.navigation-bar__inner--button & { .navigation-bar__inner--button & {
padding: 0 4px; padding: 0 4px;

View File

@ -4,7 +4,7 @@ $line-height-base: 1.67;
$line-height-title: 1.33; $line-height-title: 1.33;
$font-size-monospace: 0.85em; $font-size-monospace: 0.85em;
$code-bg: rgba(0, 0, 0, 0.05); $code-bg: rgba(0, 0, 0, 0.05);
$info-bg: transparentize(#ffa000, 0.85); $info-bg: transparentize(#f90, 0.85);
$code-border-radius: 2px; $code-border-radius: 2px;
$link-color: #0c93e4; $link-color: #0c93e4;
$error-color: #f20; $error-color: #f20;

View File

@ -44,14 +44,14 @@
<input class="hidden-file" id="import-disk-file-input" type="file" @change="onImportFile"> <input class="hidden-file" id="import-disk-file-input" type="file" @change="onImportFile">
<label class="menu-entry button flex flex--row flex--align-center" for="import-disk-file-input"> <label class="menu-entry button flex flex--row flex--align-center" for="import-disk-file-input">
<div class="menu-entry__icon flex flex--column flex--center"> <div class="menu-entry__icon flex flex--column flex--center">
<icon-hard-disk></icon-hard-disk> <icon-content-save></icon-content-save>
</div> </div>
<div class="flex flex--column"> <div class="flex flex--column">
Import from disk Import from disk
</div> </div>
</label> </label>
<menu-entry @click.native="setPanel('export')"> <menu-entry @click.native="setPanel('export')">
<icon-hard-disk slot="icon"></icon-hard-disk> <icon-content-save slot="icon"></icon-content-save>
Export to disk Export to disk
</menu-entry> </menu-entry>
<hr> <hr>

View File

@ -19,14 +19,14 @@
<input class="hidden-file" id="import-backup-file-input" type="file" @change="onImportBackup"> <input class="hidden-file" id="import-backup-file-input" type="file" @change="onImportBackup">
<label class="menu-entry button flex flex--row flex--align-center" for="import-backup-file-input"> <label class="menu-entry button flex flex--row flex--align-center" for="import-backup-file-input">
<div class="menu-entry__icon flex flex--column flex--center"> <div class="menu-entry__icon flex flex--column flex--center">
<icon-hard-disk></icon-hard-disk> <icon-content-save></icon-content-save>
</div> </div>
<div class="flex flex--column"> <div class="flex flex--column">
Import backup Import backup
</div> </div>
</label> </label>
<menu-entry href="#exportBackup=true" target="_blank"> <menu-entry href="#exportBackup=true" target="_blank">
<icon-hard-disk slot="icon"></icon-hard-disk> <icon-content-save slot="icon"></icon-content-save>
Export backup Export backup
</menu-entry> </menu-entry>
<hr> <hr>

View File

@ -21,6 +21,9 @@
</div> </div>
</div> </div>
</div> </div>
<div class="modal__info" v-if="publishLocations.length">
<b>Note:</b> Removing a synchronized location won't delete any file.
</div>
<div class="modal__button-bar"> <div class="modal__button-bar">
<button class="button" @click="config.reject()">Cancel</button> <button class="button" @click="config.reject()">Cancel</button>
<button class="button" @click="config.resolve()">Ok</button> <button class="button" @click="config.resolve()">Ok</button>

View File

@ -21,6 +21,9 @@
</div> </div>
</div> </div>
</div> </div>
<div class="modal__info" v-if="syncLocations.length">
<b>Note:</b> Removing a synchronized location won't delete any file.
</div>
<div class="modal__button-bar"> <div class="modal__button-bar">
<button class="button" @click="config.reject()">Cancel</button> <button class="button" @click="config.reject()">Cancel</button>
<button class="button" @click="config.resolve()">Ok</button> <button class="button" @click="config.resolve()">Ok</button>

View File

@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
<path d="M15,9H5V5H15M12,19C10.34,19 9,17.66 9,16C9,14.34 10.34,13 12,13C13.66,13 15,14.34 15,16C15,17.66 13.66,19 12,19M17,3H5C3.89,3 3,3.9 3,5V19C3,20.1 3.9,21 5,21H19C20.1,21 21,20.1 21,19V7L17,3Z" />
</svg>
</template>

View File

@ -1,5 +1,5 @@
<template> <template>
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="-1 -1 25 25"> <svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="-2 -2 26 26">
<path d="M15,7H20.5L15,1.5V7M8,0H16L22,6V18C22,19.1 21.1,20 20,20H8C6.89,20 6,19.1 6,18V2C6,0.9 6.9,0 8,0M4,4V22H20V24H4C2.9,24 2,23.1 2,22V4H4Z" /> <path d="M15,7H20.5L15,1.5V7M8,0H16L22,6V18C22,19.1 21.1,20 20,20H8C6.89,20 6,19.1 6,18V2C6,0.9 6.9,0 8,0M4,4V22H20V24H4C2.9,24 2,23.1 2,22V4H4Z" />
</svg> </svg>
</template> </template>

View File

@ -1,5 +0,0 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
<path d="M 6,2L 18,2C 19.1046,2 20,2.89543 20,4L 20,20C 20,21.1046 19.1046,22 18,22L 6,22C 4.89543,22 4,21.1046 4,20L 4,4C 4,2.89543 4.89543,2 6,2 Z M 12,4.00001C 8.68629,4.00001 5.99999,6.6863 5.99999,10C 5.99999,13.3137 8.68629,16 12.1022,15.9992L 11.2221,13.7674C 10.946,13.2891 11.1099,12.6775 11.5882,12.4013L 12.4542,11.9013C 12.9325,11.6252 13.5441,11.7891 13.8202,12.2674L 15.7446,14.6884C 17.1194,13.5889 18,11.8973 18,10C 18,6.6863 15.3137,4.00001 12,4.00001 Z M 12,9.00001C 12.5523,9.00001 13,9.44773 13,10C 13,10.5523 12.5523,11 12,11C 11.4477,11 11,10.5523 11,10C 11,9.44773 11.4477,9.00001 12,9.00001 Z M 7,18C 6.44771,18 6,18.4477 6,19C 6,19.5523 6.44771,20 7,20C 7.55228,20 8,19.5523 8,19C 8,18.4477 7.55228,18 7,18 Z M 12.0882,13.2674L 14.5757,19.5759L 17.1738,18.0759L 12.9542,12.7674L 12.0882,13.2674 Z "/>
</svg>
</template>

View File

@ -1,5 +1,5 @@
<template> <template>
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24"> <svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
<path fill="#000000" fill-opacity="1" stroke-linejoin="round" d="M 10.5858,13.4142C 10.9763,13.8047 10.9763,14.4379 10.5858,14.8284C 10.1952,15.2189 9.56207,15.2189 9.17154,14.8284C 7.21892,12.8758 7.21892,9.70995 9.17154,7.75733L 9.17157,7.75736L 12.707,4.2219C 14.6596,2.26928 17.8255,2.26929 19.7781,4.2219C 21.7307,6.17452 21.7307,9.34034 19.7781,11.293L 18.2925,12.7785C 18.3008,11.9583 18.1659,11.1368 17.8876,10.355L 18.3639,9.87865C 19.5355,8.70708 19.5355,6.80759 18.3639,5.63602C 17.1923,4.46445 15.2929,4.46445 14.1213,5.63602L 10.5858,9.17155C 9.41419,10.3431 9.41419,12.2426 10.5858,13.4142 Z M 13.4142,9.17155C 13.8047,8.78103 14.4379,8.78103 14.8284,9.17155C 16.781,11.1242 16.781,14.29 14.8284,16.2426L 14.8284,16.2426L 11.2929,19.7782C 9.34026,21.7308 6.17444,21.7308 4.22182,19.7782C 2.26921,17.8255 2.2692,14.6597 4.22182,12.7071L 5.70744,11.2215C 5.69913,12.0417 5.8341,12.8631 6.11234,13.645L 5.63601,14.1213C 4.46444,15.2929 4.46444,17.1924 5.63601,18.3639C 6.80758,19.5355 8.70708,19.5355 9.87865,18.3639L 13.4142,14.8284C 14.5858,13.6568 14.5858,11.7573 13.4142,10.5858C 13.0237,10.1952 13.0237,9.56207 13.4142,9.17155 Z " /> <path d="M 10.5858,13.4142C 10.9763,13.8047 10.9763,14.4379 10.5858,14.8284C 10.1952,15.2189 9.56207,15.2189 9.17154,14.8284C 7.21892,12.8758 7.21892,9.70995 9.17154,7.75733L 9.17157,7.75736L 12.707,4.2219C 14.6596,2.26928 17.8255,2.26929 19.7781,4.2219C 21.7307,6.17452 21.7307,9.34034 19.7781,11.293L 18.2925,12.7785C 18.3008,11.9583 18.1659,11.1368 17.8876,10.355L 18.3639,9.87865C 19.5355,8.70708 19.5355,6.80759 18.3639,5.63602C 17.1923,4.46445 15.2929,4.46445 14.1213,5.63602L 10.5858,9.17155C 9.41419,10.3431 9.41419,12.2426 10.5858,13.4142 Z M 13.4142,9.17155C 13.8047,8.78103 14.4379,8.78103 14.8284,9.17155C 16.781,11.1242 16.781,14.29 14.8284,16.2426L 14.8284,16.2426L 11.2929,19.7782C 9.34026,21.7308 6.17444,21.7308 4.22182,19.7782C 2.26921,17.8255 2.2692,14.6597 4.22182,12.7071L 5.70744,11.2215C 5.69913,12.0417 5.8341,12.8631 6.11234,13.645L 5.63601,14.1213C 4.46444,15.2929 4.46444,17.1924 5.63601,18.3639C 6.80758,19.5355 8.70708,19.5355 9.87865,18.3639L 13.4142,14.8284C 14.5858,13.6568 14.5858,11.7573 13.4142,10.5858C 13.0237,10.1952 13.0237,9.56207 13.4142,9.17155 Z " />
</svg> </svg>
</template> </template>

5
src/icons/Redo.vue Normal file
View File

@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
<path d="M18.4,10.6C16.55,9 14.15,8 11.5,8C6.85,8 2.92,11.03 1.54,15.22L3.9,16C4.95,12.81 7.95,10.5 11.5,10.5C13.45,10.5 15.23,11.22 16.62,12.38L13,16H22V7L18.4,10.6Z" />
</svg>
</template>

5
src/icons/Undo.vue Normal file
View File

@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
<path d="M12.5,8C9.85,8 7.45,9 5.6,10.6L2,7V16H11L7.38,12.38C8.77,11.22 10.54,10.5 12.5,10.5C16.04,10.5 19.05,12.81 20.1,16L22.47,15.22C21.08,11.03 17.15,8 12.5,8Z" />
</svg>
</template>

View File

@ -32,7 +32,6 @@ import Sync from './Sync';
import SyncOff from './SyncOff'; import SyncOff from './SyncOff';
import Upload from './Upload'; import Upload from './Upload';
import ViewList from './ViewList'; import ViewList from './ViewList';
import HardDisk from './HardDisk';
import Download from './Download'; import Download from './Download';
import CodeTags from './CodeTags'; import CodeTags from './CodeTags';
import CodeBraces from './CodeBraces'; import CodeBraces from './CodeBraces';
@ -43,6 +42,9 @@ import SignalOff from './SignalOff';
import Folder from './Folder'; import Folder from './Folder';
import ScrollSync from './ScrollSync'; import ScrollSync from './ScrollSync';
import Printer from './Printer'; import Printer from './Printer';
import Undo from './Undo';
import Redo from './Redo';
import ContentSave from './ContentSave';
Vue.component('iconProvider', Provider); Vue.component('iconProvider', Provider);
Vue.component('iconFormatBold', FormatBold); Vue.component('iconFormatBold', FormatBold);
@ -77,7 +79,6 @@ Vue.component('iconSync', Sync);
Vue.component('iconSyncOff', SyncOff); Vue.component('iconSyncOff', SyncOff);
Vue.component('iconUpload', Upload); Vue.component('iconUpload', Upload);
Vue.component('iconViewList', ViewList); Vue.component('iconViewList', ViewList);
Vue.component('iconHardDisk', HardDisk);
Vue.component('iconDownload', Download); Vue.component('iconDownload', Download);
Vue.component('iconCodeTags', CodeTags); Vue.component('iconCodeTags', CodeTags);
Vue.component('iconCodeBraces', CodeBraces); Vue.component('iconCodeBraces', CodeBraces);
@ -88,3 +89,6 @@ Vue.component('iconSignalOff', SignalOff);
Vue.component('iconFolder', Folder); Vue.component('iconFolder', Folder);
Vue.component('iconScrollSync', ScrollSync); Vue.component('iconScrollSync', ScrollSync);
Vue.component('iconPrinter', Printer); Vue.component('iconPrinter', Printer);
Vue.component('iconUndo', Undo);
Vue.component('iconRedo', Redo);
Vue.component('iconContentSave', ContentSave);

View File

@ -448,6 +448,16 @@ const editorSvc = Object.assign(new Vue(), { // Use a vue instance as an event b
}; };
this.parsingCtx = parsingCtx; this.parsingCtx = parsingCtx;
}); });
editorEngineSvc.clEditor.undoMgr.on('undoStateChange', () => {
const canUndo = editorEngineSvc.clEditor.undoMgr.canUndo();
if (canUndo !== store.state.layout.canUndo) {
store.commit('layout/setCanUndo', canUndo);
}
const canRedo = editorEngineSvc.clEditor.undoMgr.canRedo();
if (canRedo !== store.state.layout.canRedo) {
store.commit('layout/setCanRedo', canRedo);
}
});
this.pagedownEditor = pagedown({ this.pagedownEditor = pagedown({
input: Object.create(editorEngineSvc.clEditor), input: Object.create(editorEngineSvc.clEditor),
}); });

View File

@ -74,23 +74,15 @@ export default providerRegistry.register({
if (!path) { if (!path) {
return null; return null;
} }
let syncLocation; if (providerUtils.openFileWithLocation(store.getters['syncLocation/items'], {
// Try to find an existing sync location providerId: this.id,
store.getters['syncLocation/items'].some((existingSyncLocation) => { path,
if (existingSyncLocation.providerId === this.id && })) {
existingSyncLocation.path === path // File exists and has just been opened. Next...
) {
syncLocation = existingSyncLocation;
}
return syncLocation;
});
if (syncLocation) {
// Sync location already exists, just open the file
store.commit('file/setCurrentId', syncLocation.fileId);
return openOneFile(); return openOneFile();
} }
// Sync location does not exist, download content from Dropbox and create the file // Download content from Dropbox and create the file
syncLocation = { const syncLocation = {
path, path,
providerId: this.id, providerId: this.id,
sub: token.sub, sub: token.sub,

View File

@ -56,30 +56,22 @@ export default providerRegistry.register({
driveFileId: driveFile.id, driveFileId: driveFile.id,
})); }));
}, },
openFiles(token, files) { openFiles(token, driveFiles) {
const openOneFile = () => { const openOneFile = () => {
const file = files.pop(); const driveFile = driveFiles.pop();
if (!file) { if (!driveFile) {
return null; return null;
} }
let syncLocation; if (providerUtils.openFileWithLocation(store.getters['syncLocation/items'], {
// Try to find an existing sync location providerId: this.id,
store.getters['syncLocation/items'].some((existingSyncLocation) => { driveFileId: driveFile.id,
if (existingSyncLocation.providerId === this.id && })) {
existingSyncLocation.driveFileId === file.id // File exists and has just been opened. Next...
) {
syncLocation = existingSyncLocation;
}
return syncLocation;
});
if (syncLocation) {
// Sync location already exists, just open the file
store.commit('file/setCurrentId', syncLocation.fileId);
return openOneFile(); return openOneFile();
} }
// Sync location does not exist, download content from Google Drive and create the file // Download content from Google Drive and create the file
syncLocation = { const syncLocation = {
driveFileId: file.id, driveFileId: driveFile.id,
providerId: this.id, providerId: this.id,
sub: token.sub, sub: token.sub,
}; };
@ -93,7 +85,7 @@ export default providerRegistry.register({
}); });
store.commit('file/setItem', { store.commit('file/setItem', {
id, id,
name: utils.sanitizeName(file.name), name: utils.sanitizeName(driveFile.name),
parentId: store.getters['file/current'].parentId, parentId: store.getters['file/current'].parentId,
}); });
store.commit('syncLocation/setItem', { store.commit('syncLocation/setItem', {
@ -104,7 +96,7 @@ export default providerRegistry.register({
store.commit('file/setCurrentId', id); store.commit('file/setCurrentId', id);
store.dispatch('notification/info', `${store.getters['file/current'].name} was imported from Google Drive.`); store.dispatch('notification/info', `${store.getters['file/current'].name} was imported from Google Drive.`);
}, () => { }, () => {
store.dispatch('notification/error', `Could not open file ${file.id}.`); store.dispatch('notification/error', `Could not open file ${driveFile.id}.`);
}) })
.then(() => openOneFile()); .then(() => openOneFile());
}; };

View File

@ -57,4 +57,28 @@ export default {
})); }));
return result; return result;
}, },
/**
* Find and open a file location that fits the criteria
*/
openFileWithLocation(allLocations, criteria) {
return allLocations.some((location) => {
// If every field fits the criteria
if (Object.keys(criteria).every(key => criteria[key] === location[key])) {
// Found one location that fits, open it if it exists
const file = store.state.file.itemMap[location.fileId];
if (file) {
store.commit('file/setCurrentId', file.id);
// If file is in the trash, restore it
if (file.parentId === 'trash') {
store.commit('file/patchItem', {
...file,
parentId: null,
});
}
return true;
}
}
return false;
});
},
}; };

View File

@ -2,7 +2,7 @@ const editorMinWidth = 320;
const minPadding = 20; const minPadding = 20;
const previewButtonWidth = 55; const previewButtonWidth = 55;
const editorTopPadding = 10; const editorTopPadding = 10;
const navigationBarEditButtonsWidth = 36 * 12; // 12 buttons const navigationBarEditButtonsWidth = (36 * 14) + 8; // 14 buttons + 1 spacer
const navigationBarLeftButtonWidth = 38 + 4 + 15; const navigationBarLeftButtonWidth = 38 + 4 + 15;
const navigationBarRightButtonWidth = 38 + 8; const navigationBarRightButtonWidth = 38 + 8;
const navigationBarSpinnerWidth = 24 + 8 + 5; // 5 for left margin const navigationBarSpinnerWidth = 24 + 8 + 5; // 5 for left margin
@ -118,10 +118,18 @@ function computeStyles(state, localSettings, getters, styles = {
export default { export default {
namespaced: true, namespaced: true,
state: { state: {
canUndo: false,
canRedo: false,
bodyWidth: 0, bodyWidth: 0,
bodyHeight: 0, bodyHeight: 0,
}, },
mutations: { mutations: {
setCanUndo: (state, value) => {
state.canUndo = value;
},
setCanRedo: (state, value) => {
state.canRedo = value;
},
updateBodySize: (state) => { updateBodySize: (state) => {
state.bodyWidth = document.body.clientWidth; state.bodyWidth = document.body.clientWidth;
state.bodyHeight = document.body.clientHeight; state.bodyHeight = document.body.clientHeight;