Fixed main provider synchronization
This commit is contained in:
parent
79b954f0f1
commit
f8f3a87559
11
index.html
11
index.html
@ -3,6 +3,17 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>StackEdit</title>
|
<title>StackEdit</title>
|
||||||
|
<link rel="canonical" href="https://stackedit.io/app">
|
||||||
|
<!-- <link rel="icon" href="res-min/img/stackedit-32.ico" type="image/x-icon"> -->
|
||||||
|
<!-- <link rel="icon" sizes="192x192" href="res-min/img/logo-highres.png"> -->
|
||||||
|
<!-- <link rel="shortcut icon" href="res-min/img/stackedit-32.ico" type="image/x-icon"> -->
|
||||||
|
<!-- <link rel="shortcut icon" sizes="192x192" href="res-min/img/logo-highres.png"> -->
|
||||||
|
<!-- <link rel="apple-touch-icon-precomposed" sizes="152x152" href="res-min/img/logo-ipad-retina.png"> -->
|
||||||
|
<meta name="description" content="Free, open-source, full-featured Markdown editor.">
|
||||||
|
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
|
||||||
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
10
index.js
10
index.js
@ -1,3 +1,5 @@
|
|||||||
|
process.env.NODE_ENV = 'production';
|
||||||
|
|
||||||
var cluster = require('cluster');
|
var cluster = require('cluster');
|
||||||
var http = require('http');
|
var http = require('http');
|
||||||
var https = require('https');
|
var https = require('https');
|
||||||
@ -7,13 +9,13 @@ var app = express();
|
|||||||
|
|
||||||
require('./server')(app);
|
require('./server')(app);
|
||||||
|
|
||||||
var port = process.env.PORT || 8080;
|
var port = parseInt(process.env.PORT || 8080, 10);
|
||||||
if(port === 443) {
|
if(port === 443) {
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var credentials = {
|
var credentials = {
|
||||||
key: fs.readFileSync(path.join(__dirname, '/../../shared/config/ssl.key'), 'utf8'),
|
key: fs.readFileSync(path.join(__dirname, 'ssl.key'), 'utf8'),
|
||||||
cert: fs.readFileSync(path.join(__dirname, '/../../shared/config/ssl.crt'), 'utf8'),
|
cert: fs.readFileSync(path.join(__dirname, 'ssl.crt'), 'utf8'),
|
||||||
ca: fs.readFileSync(path.join(__dirname, '/../../shared/config/ssl.ca'), 'utf8').split('\n\n')
|
ca: fs.readFileSync(path.join(__dirname, 'ssl.ca'), 'utf8').split('\n\n')
|
||||||
};
|
};
|
||||||
var httpsServer = https.createServer(credentials, app);
|
var httpsServer = https.createServer(credentials, app);
|
||||||
httpsServer.listen(port, null, function() {
|
httpsServer.listen(port, null, function() {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "stackedit",
|
"name": "stackedit",
|
||||||
"version": "5.0.1",
|
"version": "5.0.2",
|
||||||
"description": "Free, open-source, full-featured Markdown editor",
|
"description": "Free, open-source, full-featured Markdown editor",
|
||||||
"author": "Benoit Schweblin",
|
"author": "Benoit Schweblin",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
@ -39,7 +39,7 @@
|
|||||||
"raw-loader": "^0.5.1",
|
"raw-loader": "^0.5.1",
|
||||||
"request": "^2.82.0",
|
"request": "^2.82.0",
|
||||||
"serve-static": "^1.12.6",
|
"serve-static": "^1.12.6",
|
||||||
"stackedit": "^4.3.16",
|
"stackedit": "^4.3.17",
|
||||||
"vue": "^2.3.3",
|
"vue": "^2.3.3",
|
||||||
"vuex": "^2.3.1"
|
"vuex": "^2.3.1"
|
||||||
},
|
},
|
||||||
|
@ -3,6 +3,8 @@ var serveStatic = require('serve-static');
|
|||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
|
||||||
module.exports = function (app) {
|
module.exports = function (app) {
|
||||||
|
// Use gzip compression
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
// Force HTTPS on stackedit.io
|
// Force HTTPS on stackedit.io
|
||||||
app.all('*', function(req, res, next) {
|
app.all('*', function(req, res, next) {
|
||||||
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') {
|
||||||
@ -12,12 +14,22 @@ module.exports = function (app) {
|
|||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Use gzip compression
|
|
||||||
app.use(compression());
|
app.use(compression());
|
||||||
|
}
|
||||||
|
|
||||||
app.post('/pdfExport', require('./pdf').export);
|
|
||||||
app.get('/oauth2/githubToken', require('./github').githubToken);
|
app.get('/oauth2/githubToken', require('./github').githubToken);
|
||||||
|
app.post('/pdfExport', require('stackedit/app/pdf').export);
|
||||||
|
app.post('/sshPublish', require('stackedit/app/ssh').publish);
|
||||||
|
app.post('/picasaImportImg', require('stackedit/app/picasa').importImg);
|
||||||
|
app.get('/downloadImport', require('stackedit/app/download').importPublic);
|
||||||
|
|
||||||
|
// Serve callback.html in /app
|
||||||
|
app.get('/oauth2/callback', function(req, res) {
|
||||||
|
res.sendFile(path.join(__dirname, '../static/oauth2/callback.html'));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Serve static resources
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
// Serve landing.html in /
|
// Serve landing.html in /
|
||||||
app.get('/', function(req, res) {
|
app.get('/', function(req, res) {
|
||||||
res.sendFile(require.resolve('stackedit/views/landing.html'));
|
res.sendFile(require.resolve('stackedit/views/landing.html'));
|
||||||
@ -34,12 +46,7 @@ module.exports = function (app) {
|
|||||||
app.get('/app', function(req, res) {
|
app.get('/app', function(req, res) {
|
||||||
res.sendFile(path.join(__dirname, '../dist/index.html'));
|
res.sendFile(path.join(__dirname, '../dist/index.html'));
|
||||||
});
|
});
|
||||||
// Serve callback.html in /app
|
|
||||||
app.get('/oauth2/callback', function(req, res) {
|
|
||||||
res.sendFile(path.join(__dirname, '../dist/static/oauth2/callback.html'));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Serve static resources
|
|
||||||
app.use(serveStatic(path.join(__dirname, '../dist'))); // v5
|
app.use(serveStatic(path.join(__dirname, '../dist'))); // v5
|
||||||
app.use(serveStatic(path.dirname(require.resolve('stackedit/public/cache.manifest')))); // v4
|
app.use(serveStatic(path.dirname(require.resolve('stackedit/public/cache.manifest')))); // v4
|
||||||
|
|
||||||
@ -47,4 +54,5 @@ module.exports = function (app) {
|
|||||||
app.use(function(req, res) {
|
app.use(function(req, res) {
|
||||||
res.status(404).sendFile(require.resolve('stackedit/views/error_404.html'));
|
res.status(404).sendFile(require.resolve('stackedit/views/error_404.html'));
|
||||||
});
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 2.1 KiB |
@ -372,7 +372,7 @@ $t: 3000ms;
|
|||||||
.navigation-bar__spinner {
|
.navigation-bar__spinner {
|
||||||
width: 22px;
|
width: 22px;
|
||||||
margin: 8px 0 0 8px;
|
margin: 8px 0 0 8px;
|
||||||
color: rgba(255, 255, 255, 0.67);
|
color: #b2b2b2;
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
width: 22px;
|
width: 22px;
|
||||||
|
@ -15,6 +15,16 @@
|
|||||||
<div>Reset application</div>
|
<div>Reset application</div>
|
||||||
<span>Sign out and clean local data.</span>
|
<span>Sign out and clean local data.</span>
|
||||||
</menu-entry>
|
</menu-entry>
|
||||||
|
<hr>
|
||||||
|
<a href="editor" target="_blank" class="menu-entry button flex flex--row flex--align-center">
|
||||||
|
<div class="menu-entry__icon flex flex--column flex--center">
|
||||||
|
<icon-alert></icon-alert>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex--column">
|
||||||
|
<div>StackEdit v4</div>
|
||||||
|
<span>Deprecated.</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import 'babel-polyfill';
|
|||||||
import 'indexeddbshim/dist/indexeddbshim';
|
import 'indexeddbshim/dist/indexeddbshim';
|
||||||
import utils from './utils';
|
import utils from './utils';
|
||||||
import store from '../store';
|
import store from '../store';
|
||||||
|
import welcomeFile from '../data/welcomeFile.md';
|
||||||
|
|
||||||
const indexedDB = window.indexedDB;
|
const indexedDB = window.indexedDB;
|
||||||
const dbVersion = 1;
|
const dbVersion = 1;
|
||||||
@ -91,7 +92,7 @@ const contentTypes = {
|
|||||||
syncedContent: true,
|
syncedContent: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
const localDbSvc = {
|
||||||
lastTx: 0,
|
lastTx: 0,
|
||||||
hashMap,
|
hashMap,
|
||||||
connection: new Connection(),
|
connection: new Connection(),
|
||||||
@ -297,3 +298,77 @@ export default {
|
|||||||
}, () => store.dispatch('notification/error', 'Could not delete local database.'));
|
}, () => store.dispatch('notification/error', 'Could not delete local database.'));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const loader = type => fileId => localDbSvc.loadItem(`${fileId}/${type}`)
|
||||||
|
// Item does not exist, create it
|
||||||
|
.catch(() => store.commit(`${type}/setItem`, {
|
||||||
|
id: `${fileId}/${type}`,
|
||||||
|
}));
|
||||||
|
localDbSvc.loadSyncedContent = loader('syncedContent');
|
||||||
|
localDbSvc.loadContentState = loader('contentState');
|
||||||
|
|
||||||
|
const ifNoId = cb => (obj) => {
|
||||||
|
if (obj.id) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
return cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load the DB on boot
|
||||||
|
localDbSvc.sync()
|
||||||
|
// And watch file changing
|
||||||
|
.then(() => store.watch(
|
||||||
|
() => store.getters['file/current'].id,
|
||||||
|
() => Promise.resolve(store.getters['file/current'])
|
||||||
|
// If current file has no ID, get the most recent file
|
||||||
|
.then(ifNoId(() => store.getters['file/lastOpened']))
|
||||||
|
// If still no ID, create a new file
|
||||||
|
.then(ifNoId(() => {
|
||||||
|
const id = utils.uid();
|
||||||
|
store.commit('content/setItem', {
|
||||||
|
id: `${id}/content`,
|
||||||
|
text: welcomeFile,
|
||||||
|
});
|
||||||
|
store.commit('file/setItem', {
|
||||||
|
id,
|
||||||
|
name: 'Welcome file',
|
||||||
|
});
|
||||||
|
return store.state.file.itemMap[id];
|
||||||
|
}))
|
||||||
|
.then((currentFile) => {
|
||||||
|
// Fix current file ID
|
||||||
|
if (store.getters['file/current'].id !== currentFile.id) {
|
||||||
|
store.commit('file/setCurrentId', currentFile.id);
|
||||||
|
// Wait for the next watch tick
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
// Load contentState from DB
|
||||||
|
.then(() => localDbSvc.loadContentState(currentFile.id))
|
||||||
|
// Load syncedContent from DB
|
||||||
|
.then(() => localDbSvc.loadSyncedContent(currentFile.id))
|
||||||
|
// Load content from DB
|
||||||
|
.then(() => localDbSvc.loadItem(`${currentFile.id}/content`))
|
||||||
|
.then(
|
||||||
|
// Success, set last opened file
|
||||||
|
() => store.dispatch('data/setLastOpenedId', currentFile.id),
|
||||||
|
(err) => {
|
||||||
|
// Failure (content is not available), go back to previous file
|
||||||
|
const lastOpenedFile = store.getters['file/lastOpened'];
|
||||||
|
store.commit('file/setCurrentId', lastOpenedFile.id);
|
||||||
|
throw err;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err); // eslint-disable-line no-console
|
||||||
|
store.dispatch('notification/error', err);
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Sync local DB periodically
|
||||||
|
utils.setInterval(() => localDbSvc.sync(), 1000);
|
||||||
|
|
||||||
|
export default localDbSvc;
|
||||||
|
@ -62,8 +62,7 @@ export default providerRegistry.register({
|
|||||||
.then(() => syncData);
|
.then(() => syncData);
|
||||||
},
|
},
|
||||||
downloadContent(token, syncLocation) {
|
downloadContent(token, syncLocation) {
|
||||||
return this.downloadData(token, `${syncLocation.fileId}/content`)
|
return this.downloadData(token, `${syncLocation.fileId}/content`);
|
||||||
.then(() => syncLocation);
|
|
||||||
},
|
},
|
||||||
downloadData(token, dataId) {
|
downloadData(token, dataId) {
|
||||||
const syncData = store.getters['data/syncDataByItemId'][dataId];
|
const syncData = store.getters['data/syncDataByItemId'][dataId];
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import localDbSvc from './localDbSvc';
|
import localDbSvc from './localDbSvc';
|
||||||
import store from '../store';
|
import store from '../store';
|
||||||
import welcomeFile from '../data/welcomeFile.md';
|
|
||||||
import utils from './utils';
|
import utils from './utils';
|
||||||
import diffUtils from './diffUtils';
|
import diffUtils from './diffUtils';
|
||||||
import providerRegistry from './providers/providerRegistry';
|
import providerRegistry from './providers/providerRegistry';
|
||||||
@ -54,15 +53,6 @@ function cleanSyncedContent(syncedContent) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const loader = type => fileId => localDbSvc.loadItem(`${fileId}/${type}`)
|
|
||||||
// Item does not exist, create it
|
|
||||||
.catch(() => store.commit(`${type}/setItem`, {
|
|
||||||
id: `${fileId}/${type}`,
|
|
||||||
}));
|
|
||||||
const loadContent = loader('content');
|
|
||||||
const loadSyncedContent = loader('syncedContent');
|
|
||||||
const loadContentState = loader('contentState');
|
|
||||||
|
|
||||||
function applyChanges(changes) {
|
function applyChanges(changes) {
|
||||||
const token = mainProvider.getToken();
|
const token = mainProvider.getToken();
|
||||||
const storeItemMap = { ...store.getters.allItemMap };
|
const storeItemMap = { ...store.getters.allItemMap };
|
||||||
@ -70,6 +60,8 @@ function applyChanges(changes) {
|
|||||||
let syncDataChanged = false;
|
let syncDataChanged = false;
|
||||||
|
|
||||||
changes.forEach((change) => {
|
changes.forEach((change) => {
|
||||||
|
// Ignore items that belong to another user (like settings)
|
||||||
|
if (!change.item || !change.item.sub || change.item.sub === token.sub) {
|
||||||
const existingSyncData = syncData[change.fileId];
|
const existingSyncData = syncData[change.fileId];
|
||||||
const existingItem = existingSyncData && storeItemMap[existingSyncData.itemId];
|
const existingItem = existingSyncData && storeItemMap[existingSyncData.itemId];
|
||||||
if (change.removed && existingSyncData) {
|
if (change.removed && existingSyncData) {
|
||||||
@ -80,10 +72,7 @@ function applyChanges(changes) {
|
|||||||
}
|
}
|
||||||
delete syncData[change.fileId];
|
delete syncData[change.fileId];
|
||||||
syncDataChanged = true;
|
syncDataChanged = true;
|
||||||
} else if (!change.removed && change.item && change.item.hash && (
|
} else if (!change.removed && change.item && change.item.hash) {
|
||||||
// Ignore items that belong to another user (like settings)
|
|
||||||
!change.item.sub || change.item.sub === token.sub
|
|
||||||
)) {
|
|
||||||
if (!existingSyncData || (existingSyncData.hash !== change.item.hash && (
|
if (!existingSyncData || (existingSyncData.hash !== change.item.hash && (
|
||||||
!existingItem || existingItem.hash !== change.item.hash
|
!existingItem || existingItem.hash !== change.item.hash
|
||||||
))) {
|
))) {
|
||||||
@ -96,6 +85,7 @@ function applyChanges(changes) {
|
|||||||
syncData[change.fileId] = change.syncData;
|
syncData[change.fileId] = change.syncData;
|
||||||
syncDataChanged = true;
|
syncDataChanged = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (syncDataChanged) {
|
if (syncDataChanged) {
|
||||||
@ -121,7 +111,7 @@ function createSyncLocation(syncLocation) {
|
|||||||
...content,
|
...content,
|
||||||
history: [content.hash],
|
history: [content.hash],
|
||||||
}, syncLocation)
|
}, syncLocation)
|
||||||
.then(syncLocationToStore => loadSyncedContent(fileId)
|
.then(syncLocationToStore => localDbSvc.loadSyncedContent(fileId)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const newSyncedContent = utils.deepCopy(
|
const newSyncedContent = utils.deepCopy(
|
||||||
store.state.syncedContent.itemMap[`${fileId}/syncedContent`]);
|
store.state.syncedContent.itemMap[`${fileId}/syncedContent`]);
|
||||||
@ -137,9 +127,11 @@ function createSyncLocation(syncLocation) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncFile(fileId) {
|
function syncFile(fileId, needSyncRestartParam = false) {
|
||||||
return loadSyncedContent(fileId)
|
let needSyncRestart = needSyncRestartParam;
|
||||||
.then(() => loadContent(fileId))
|
return localDbSvc.loadSyncedContent(fileId)
|
||||||
|
.then(() => localDbSvc.loadItem(`${fileId}/content`)
|
||||||
|
.catch(() => {})) // Item may not exist if content has not been downloaded yet
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const getContent = () => store.state.content.itemMap[`${fileId}/content`];
|
const getContent = () => store.state.content.itemMap[`${fileId}/content`];
|
||||||
const getSyncedContent = () => store.state.syncedContent.itemMap[`${fileId}/syncedContent`];
|
const getSyncedContent = () => store.state.syncedContent.itemMap[`${fileId}/syncedContent`];
|
||||||
@ -176,6 +168,9 @@ function syncFile(fileId) {
|
|||||||
const syncHistoryItem = getSyncHistoryItem(syncLocation.id);
|
const syncHistoryItem = getSyncHistoryItem(syncLocation.id);
|
||||||
let mergedContent = (() => {
|
let mergedContent = (() => {
|
||||||
const clientContent = utils.deepCopy(getContent());
|
const clientContent = utils.deepCopy(getContent());
|
||||||
|
if (!clientContent) {
|
||||||
|
return utils.deepCopy(serverContent);
|
||||||
|
}
|
||||||
if (!serverContent) {
|
if (!serverContent) {
|
||||||
// Sync location has not been created yet
|
// Sync location has not been created yet
|
||||||
return clientContent;
|
return clientContent;
|
||||||
@ -200,10 +195,17 @@ function syncFile(fileId) {
|
|||||||
return diffUtils.mergeContent(serverContent, clientContent, lastMergedContent);
|
return diffUtils.mergeContent(serverContent, clientContent, lastMergedContent);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Update content in store
|
if (!mergedContent) {
|
||||||
store.commit('content/patchItem', {
|
errorLocations[syncLocation.id] = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update or set content in store
|
||||||
|
delete mergedContent.history;
|
||||||
|
store.commit('content/setItem', {
|
||||||
id: `${fileId}/content`,
|
id: `${fileId}/content`,
|
||||||
...mergedContent,
|
...mergedContent,
|
||||||
|
hash: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Retrieve content with new `hash` and freeze it
|
// Retrieve content with new `hash` and freeze it
|
||||||
@ -272,6 +274,12 @@ function syncFile(fileId) {
|
|||||||
) {
|
) {
|
||||||
store.commit('syncLocation/patchItem', syncLocationToStore);
|
store.commit('syncLocation/patchItem', syncLocationToStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If content was just created, restart sync to create the file as well
|
||||||
|
const syncDataByItemId = store.getters['data/syncDataByItemId'];
|
||||||
|
if (!syncDataByItemId[fileId]) {
|
||||||
|
needSyncRestart = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
@ -298,10 +306,12 @@ function syncFile(fileId) {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
throw err;
|
throw err;
|
||||||
}))
|
}))
|
||||||
.catch((err) => {
|
.then(
|
||||||
|
() => needSyncRestart,
|
||||||
|
(err) => {
|
||||||
if (err && err.message === 'TOO_LATE') {
|
if (err && err.message === 'TOO_LATE') {
|
||||||
// Restart sync
|
// Restart sync
|
||||||
return syncFile(fileId);
|
return syncFile(fileId, needSyncRestart);
|
||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
@ -320,7 +330,10 @@ function syncDataItem(dataId) {
|
|||||||
.then((serverItem = null) => {
|
.then((serverItem = null) => {
|
||||||
const dataSyncData = store.getters['data/dataSyncData'][dataId];
|
const dataSyncData = store.getters['data/dataSyncData'][dataId];
|
||||||
let mergedItem = (() => {
|
let mergedItem = (() => {
|
||||||
const clientItem = utils.deepCopy(store.getters[`data/${dataId}`]);
|
const clientItem = utils.deepCopy(store.state.data.itemMap[dataId]);
|
||||||
|
if (!clientItem) {
|
||||||
|
return serverItem;
|
||||||
|
}
|
||||||
if (!serverItem) {
|
if (!serverItem) {
|
||||||
return clientItem;
|
return clientItem;
|
||||||
}
|
}
|
||||||
@ -341,6 +354,10 @@ function syncDataItem(dataId) {
|
|||||||
return clientItem;
|
return clientItem;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
if (!mergedItem) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Update item in store
|
// Update item in store
|
||||||
store.commit('data/setItem', {
|
store.commit('data/setItem', {
|
||||||
id: dataId,
|
id: dataId,
|
||||||
@ -401,7 +418,10 @@ function sync() {
|
|||||||
Object.keys(storeItemMap).some((id) => {
|
Object.keys(storeItemMap).some((id) => {
|
||||||
const item = storeItemMap[id];
|
const item = storeItemMap[id];
|
||||||
const existingSyncData = syncDataByItemId[id];
|
const existingSyncData = syncDataByItemId[id];
|
||||||
if (!existingSyncData || existingSyncData.hash !== item.hash) {
|
if ((!existingSyncData || existingSyncData.hash !== item.hash) &&
|
||||||
|
// Add file if content has been uploaded
|
||||||
|
(item.type !== 'file' || syncDataByItemId[`${id}/content`])
|
||||||
|
) {
|
||||||
result = mainProvider.saveItem(
|
result = mainProvider.saveItem(
|
||||||
mainToken,
|
mainToken,
|
||||||
// Use deepCopy to freeze objects
|
// Use deepCopy to freeze objects
|
||||||
@ -454,9 +474,12 @@ function sync() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const getOneFileIdToSync = () => {
|
const getOneFileIdToSync = () => {
|
||||||
const allContentIds = Object.keys(localDbSvc.hashMap.content);
|
const contentIds = [...new Set([
|
||||||
|
...Object.keys(localDbSvc.hashMap.content),
|
||||||
|
...store.getters['file/items'].map(file => `${file.id}/content`),
|
||||||
|
])];
|
||||||
let fileId;
|
let fileId;
|
||||||
allContentIds.some((contentId) => {
|
contentIds.some((contentId) => {
|
||||||
// Get content hash from itemMap or from localDbSvc if not loaded
|
// Get content hash from itemMap or from localDbSvc if not loaded
|
||||||
const loadedContent = store.state.content.itemMap[contentId];
|
const loadedContent = store.state.content.itemMap[contentId];
|
||||||
const hash = loadedContent ? loadedContent.hash : localDbSvc.hashMap.content[contentId];
|
const hash = loadedContent ? loadedContent.hash : localDbSvc.hashMap.content[contentId];
|
||||||
@ -470,10 +493,13 @@ function sync() {
|
|||||||
return fileId;
|
return fileId;
|
||||||
};
|
};
|
||||||
|
|
||||||
const syncNextFile = () => {
|
const syncNextFile = (needSyncRestartParam) => {
|
||||||
const fileId = getOneFileIdToSync();
|
const fileId = getOneFileIdToSync();
|
||||||
return fileId && syncFile(fileId)
|
if (!fileId) {
|
||||||
.then(() => syncNextFile());
|
return needSyncRestartParam;
|
||||||
|
}
|
||||||
|
return syncFile(fileId, needSyncRestartParam)
|
||||||
|
.then(needSyncRestart => syncNextFile(needSyncRestart));
|
||||||
};
|
};
|
||||||
|
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
@ -482,15 +508,23 @@ function sync() {
|
|||||||
.then(() => syncDataItem('settings'))
|
.then(() => syncDataItem('settings'))
|
||||||
.then(() => syncDataItem('templates'))
|
.then(() => syncDataItem('templates'))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const currentFileId = store.getters['content/current'].id;
|
const currentFileId = store.getters['file/current'].id;
|
||||||
if (currentFileId) {
|
if (currentFileId) {
|
||||||
// Sync current file first
|
// Sync current file first
|
||||||
return syncFile(currentFileId)
|
return syncFile(currentFileId)
|
||||||
.then(() => syncNextFile());
|
.then(needSyncRestart => syncNextFile(needSyncRestart));
|
||||||
}
|
}
|
||||||
return syncNextFile();
|
return syncNextFile();
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.then(
|
||||||
|
(needSyncRestart) => {
|
||||||
|
if (needSyncRestart) {
|
||||||
|
// Restart sync
|
||||||
|
return sync();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
if (err && err.message === 'TOO_LATE') {
|
if (err && err.message === 'TOO_LATE') {
|
||||||
// Restart sync
|
// Restart sync
|
||||||
return sync();
|
return sync();
|
||||||
@ -552,58 +586,6 @@ utils.setInterval(() => {
|
|||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
const ifNoId = cb => (obj) => {
|
|
||||||
if (obj.id) {
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
return cb();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load the DB on boot
|
|
||||||
localDbSvc.sync()
|
|
||||||
// And watch file changing
|
|
||||||
.then(() => store.watch(
|
|
||||||
() => store.getters['file/current'].id,
|
|
||||||
() => Promise.resolve(store.getters['file/current'])
|
|
||||||
// If current file has no ID, get the most recent file
|
|
||||||
.then(ifNoId(() => store.getters['file/lastOpened']))
|
|
||||||
// If still no ID, create a new file
|
|
||||||
.then(ifNoId(() => {
|
|
||||||
const id = utils.uid();
|
|
||||||
store.commit('content/setItem', {
|
|
||||||
id: `${id}/content`,
|
|
||||||
text: welcomeFile,
|
|
||||||
});
|
|
||||||
store.commit('file/setItem', {
|
|
||||||
id,
|
|
||||||
name: 'Welcome file',
|
|
||||||
});
|
|
||||||
return store.state.file.itemMap[id];
|
|
||||||
}))
|
|
||||||
.then((currentFile) => {
|
|
||||||
// Fix current file ID
|
|
||||||
if (store.getters['file/current'].id !== currentFile.id) {
|
|
||||||
store.commit('file/setCurrentId', currentFile.id);
|
|
||||||
// Wait for the next watch tick
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// Set last opened
|
|
||||||
store.dispatch('data/setLastOpenedId', currentFile.id);
|
|
||||||
return Promise.resolve()
|
|
||||||
// Load contentState from DB
|
|
||||||
.then(() => loadContentState(currentFile.id))
|
|
||||||
// Load syncedContent from DB
|
|
||||||
.then(() => loadSyncedContent(currentFile.id))
|
|
||||||
// Load content from DB
|
|
||||||
.then(() => localDbSvc.loadItem(`${currentFile.id}/content`));
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
immediate: true,
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Sync local DB periodically
|
|
||||||
utils.setInterval(() => localDbSvc.sync(), 1000);
|
|
||||||
|
|
||||||
// Unload contents from memory periodically
|
// Unload contents from memory periodically
|
||||||
utils.setInterval(() => {
|
utils.setInterval(() => {
|
||||||
// Wait for sync and publish to finish
|
// Wait for sync and publish to finish
|
||||||
|
@ -5945,9 +5945,9 @@ sshpk@^1.7.0:
|
|||||||
jsbn "~0.1.0"
|
jsbn "~0.1.0"
|
||||||
tweetnacl "~0.14.0"
|
tweetnacl "~0.14.0"
|
||||||
|
|
||||||
stackedit@^4.3.16:
|
stackedit@^4.3.17:
|
||||||
version "4.3.16"
|
version "4.3.17"
|
||||||
resolved "https://registry.yarnpkg.com/stackedit/-/stackedit-4.3.16.tgz#e4df6af29e70d2c45a547523e6aa4f8025c1556d"
|
resolved "https://registry.yarnpkg.com/stackedit/-/stackedit-4.3.17.tgz#3c524a625f7399d13b06706da2305a131a8df56c"
|
||||||
dependencies:
|
dependencies:
|
||||||
bower "^1.8.2"
|
bower "^1.8.2"
|
||||||
compression "^1.0.11"
|
compression "^1.0.11"
|
||||||
|
Loading…
Reference in New Issue
Block a user