CouchDB workspace (part 2)
This commit is contained in:
parent
aac305e410
commit
2374f459df
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="history side-bar__panel">
|
<div class="history side-bar__panel">
|
||||||
<div class="revision" v-for="revision in revisions" :key="revision.id">
|
<div class="revision" v-for="revision in revisionsWithSpacer" :key="revision.id">
|
||||||
<div class="history__spacer" v-if="revision.spacer"></div>
|
<div class="history__spacer" v-if="revision.spacer"></div>
|
||||||
<a class="revision__button button flex flex--row" href="javascript:void(0)" @click="open(revision)">
|
<a class="revision__button button flex flex--row" href="javascript:void(0)" @click="open(revision)">
|
||||||
<div class="revision__icon">
|
<div class="revision__icon">
|
||||||
@ -36,7 +36,7 @@ let previewClassAppliers = [];
|
|||||||
let cachedFileId;
|
let cachedFileId;
|
||||||
let revisionsPromise;
|
let revisionsPromise;
|
||||||
let revisionContentPromises;
|
let revisionContentPromises;
|
||||||
const pageSize = 50;
|
const pageSize = 30;
|
||||||
const spacerThreshold = 12 * 60 * 60 * 1000; // 12h
|
const spacerThreshold = 12 * 60 * 60 * 1000; // 12h
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -51,8 +51,11 @@ export default {
|
|||||||
}),
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
revisions() {
|
revisions() {
|
||||||
|
return this.allRevisions.slice(0, this.showCount);
|
||||||
|
},
|
||||||
|
revisionsWithSpacer() {
|
||||||
let previousCreated = 0;
|
let previousCreated = 0;
|
||||||
return this.allRevisions.slice(0, this.showCount).map((revision) => {
|
return this.revisions.map((revision) => {
|
||||||
const revisionWithSpacer = {
|
const revisionWithSpacer = {
|
||||||
...revision,
|
...revision,
|
||||||
spacer: revision.created + spacerThreshold < previousCreated,
|
spacer: revision.created + spacerThreshold < previousCreated,
|
||||||
@ -79,12 +82,12 @@ export default {
|
|||||||
let revisionContentPromise = revisionContentPromises[revision.id];
|
let revisionContentPromise = revisionContentPromises[revision.id];
|
||||||
if (!revisionContentPromise) {
|
if (!revisionContentPromise) {
|
||||||
revisionContentPromise = new Promise((resolve, reject) => {
|
revisionContentPromise = new Promise((resolve, reject) => {
|
||||||
const loginToken = this.$store.getters['workspace/loginToken'];
|
const syncToken = this.$store.getters['workspace/syncToken'];
|
||||||
const currentFile = this.$store.getters['file/current'];
|
const currentFile = this.$store.getters['file/current'];
|
||||||
this.$store.dispatch('queue/enqueue',
|
this.$store.dispatch('queue/enqueue',
|
||||||
() => Promise.resolve()
|
() => Promise.resolve()
|
||||||
.then(() => this.workspaceProvider.getRevisionContent(
|
.then(() => this.workspaceProvider.getRevisionContent(
|
||||||
loginToken, currentFile.id, revision.id))
|
syncToken, currentFile.id, revision.id))
|
||||||
.then(resolve, reject));
|
.then(resolve, reject));
|
||||||
});
|
});
|
||||||
revisionContentPromises[revision.id] = revisionContentPromise;
|
revisionContentPromises[revision.id] = revisionContentPromise;
|
||||||
@ -137,17 +140,13 @@ export default {
|
|||||||
this.setRevisionContent();
|
this.setRevisionContent();
|
||||||
cachedFileId = id;
|
cachedFileId = id;
|
||||||
revisionContentPromises = {};
|
revisionContentPromises = {};
|
||||||
const loginToken = this.$store.getters['workspace/loginToken'];
|
const syncToken = this.$store.getters['workspace/syncToken'];
|
||||||
const currentFile = this.$store.getters['file/current'];
|
const currentFile = this.$store.getters['file/current'];
|
||||||
revisionsPromise = new Promise((resolve, reject) => {
|
revisionsPromise = new Promise((resolve, reject) => {
|
||||||
this.$store.dispatch('queue/enqueue',
|
this.$store.dispatch('queue/enqueue',
|
||||||
() => Promise.resolve()
|
() => Promise.resolve()
|
||||||
.then(() => this.workspaceProvider.listRevisions(loginToken, currentFile.id))
|
.then(() => this.workspaceProvider.listRevisions(syncToken, currentFile.id))
|
||||||
.then((revisions) => {
|
.then(resolve, reject));
|
||||||
resolve(revisions.sort(
|
|
||||||
(revision1, revision2) => revision2.created - revision1.created));
|
|
||||||
})
|
|
||||||
.catch(reject));
|
|
||||||
});
|
});
|
||||||
revisionsPromise.catch(() => {
|
revisionsPromise.catch(() => {
|
||||||
cachedFileId = null;
|
cachedFileId = null;
|
||||||
@ -160,6 +159,28 @@ export default {
|
|||||||
}
|
}
|
||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
|
const loadOne = () => {
|
||||||
|
this.$store.dispatch('queue/enqueue',
|
||||||
|
() => {
|
||||||
|
let loadPromise;
|
||||||
|
this.revisions.some((revision) => {
|
||||||
|
if (!revision.created) {
|
||||||
|
const syncToken = this.$store.getters['workspace/syncToken'];
|
||||||
|
const currentFile = this.$store.getters['file/current'];
|
||||||
|
loadPromise = this.workspaceProvider.loadRevision(syncToken, currentFile.id, revision)
|
||||||
|
.then(() => loadOne());
|
||||||
|
}
|
||||||
|
return loadPromise;
|
||||||
|
});
|
||||||
|
return loadPromise;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$watch(
|
||||||
|
() => this.revisions,
|
||||||
|
() => loadOne(),
|
||||||
|
{ immediate: true });
|
||||||
|
|
||||||
// Watch diffs changes
|
// Watch diffs changes
|
||||||
this.$watch(
|
this.$watch(
|
||||||
() => this.$store.state.content.revisionContent,
|
() => this.$store.state.content.revisionContent,
|
||||||
@ -181,6 +202,8 @@ export default {
|
|||||||
this.refreshHighlighters();
|
this.refreshHighlighters();
|
||||||
// Remove event listener
|
// Remove event listener
|
||||||
window.removeEventListener('keyup', this.onKeyup);
|
window.removeEventListener('keyup', this.onKeyup);
|
||||||
|
// Cancel loading revisions
|
||||||
|
this.showCount = 0;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -140,7 +140,7 @@ export default {
|
|||||||
.catch(() => {}); // Cancel
|
.catch(() => {}); // Cancel
|
||||||
},
|
},
|
||||||
history() {
|
history() {
|
||||||
if (!this.loginToken) {
|
if (!this.syncToken) {
|
||||||
this.$store.dispatch('modal/signInForHistory', {
|
this.$store.dispatch('modal/signInForHistory', {
|
||||||
onResolve: () => googleHelper.signin()
|
onResolve: () => googleHelper.signin()
|
||||||
.then(() => syncSvc.requestSync()),
|
.then(() => syncSvc.requestSync()),
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export default () => ({
|
export default (id = null) => ({
|
||||||
id: null,
|
id,
|
||||||
type: 'content',
|
type: 'content',
|
||||||
text: '\n',
|
text: '\n',
|
||||||
properties: '\n',
|
properties: '\n',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export default () => ({
|
export default (id = null) => ({
|
||||||
id: null,
|
id,
|
||||||
type: 'contentState',
|
type: 'contentState',
|
||||||
selectionStart: 0,
|
selectionStart: 0,
|
||||||
selectionEnd: 0,
|
selectionEnd: 0,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export default () => ({
|
export default (id = null) => ({
|
||||||
id: null,
|
id,
|
||||||
type: 'file',
|
type: 'file',
|
||||||
name: '',
|
name: '',
|
||||||
parentId: null,
|
parentId: null,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export default () => ({
|
export default (id = null) => ({
|
||||||
id: null,
|
id,
|
||||||
type: 'folder',
|
type: 'folder',
|
||||||
name: '',
|
name: '',
|
||||||
parentId: null,
|
parentId: null,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export default () => ({
|
export default (id = null) => ({
|
||||||
id: null,
|
id,
|
||||||
type: 'publishLocation',
|
type: 'publishLocation',
|
||||||
providerId: null,
|
providerId: null,
|
||||||
fileId: null,
|
fileId: null,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export default () => ({
|
export default (id = null) => ({
|
||||||
id: null,
|
id,
|
||||||
type: 'syncLocation',
|
type: 'syncLocation',
|
||||||
providerId: null,
|
providerId: null,
|
||||||
fileId: null,
|
fileId: null,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export default () => ({
|
export default (id = null) => ({
|
||||||
id: null,
|
id,
|
||||||
type: 'syncedContent',
|
type: 'syncedContent',
|
||||||
historyData: {},
|
historyData: {},
|
||||||
syncHistory: {},
|
syncHistory: {},
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import store from '../../store';
|
import store from '../../store';
|
||||||
import couchdbHelper from './helpers/couchdbHelper';
|
import couchdbHelper from './helpers/couchdbHelper';
|
||||||
import providerRegistry from './providerRegistry';
|
import providerRegistry from './providerRegistry';
|
||||||
|
import providerUtils from './providerUtils';
|
||||||
import utils from '../utils';
|
import utils from '../utils';
|
||||||
|
|
||||||
export default providerRegistry.register({
|
export default providerRegistry.register({
|
||||||
@ -56,32 +57,169 @@ export default providerRegistry.register({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
getChanges() {
|
getChanges() {
|
||||||
const workspace = store.getters['workspace/currentWorkspace'];
|
|
||||||
const syncToken = store.getters['workspace/syncToken'];
|
const syncToken = store.getters['workspace/syncToken'];
|
||||||
const lastSeq = store.getters['data/localSettings'].syncLastSeq;
|
const lastSeq = store.getters['data/localSettings'].syncLastSeq;
|
||||||
return couchdbHelper.getChanges(syncToken, lastSeq, true)
|
return couchdbHelper.getChanges(syncToken, lastSeq)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
const changes = result.changes.filter((change) => {
|
const changes = result.changes.filter((change) => {
|
||||||
if (change.file) {
|
if (!change.deleted && change.doc) {
|
||||||
// Parse item from file name
|
change.item = change.doc.item;
|
||||||
try {
|
if (!change.item || !change.item.id || !change.item.type) {
|
||||||
change.item = JSON.parse(change.file.name);
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Build sync data
|
// Build sync data
|
||||||
change.syncData = {
|
change.syncData = {
|
||||||
id: change.fileId,
|
id: change.id,
|
||||||
itemId: change.item.id,
|
itemId: change.item.id,
|
||||||
type: change.item.type,
|
type: change.item.type,
|
||||||
hash: change.item.hash,
|
hash: change.item.hash,
|
||||||
|
rev: change.doc._rev, // eslint-disable-line no-underscore-dangle
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
change.syncDataId = change.fileId;
|
change.syncDataId = change.id;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
changes.startPageToken = result.startPageToken;
|
changes.lastSeq = result.lastSeq;
|
||||||
return changes;
|
return changes;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
setAppliedChanges(changes) {
|
||||||
|
store.dispatch('data/patchLocalSettings', {
|
||||||
|
syncLastSeq: changes.lastSeq,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
saveSimpleItem(item, syncData) {
|
||||||
|
const syncToken = store.getters['workspace/syncToken'];
|
||||||
|
return couchdbHelper.uploadDocument(
|
||||||
|
syncToken,
|
||||||
|
item,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
syncData && syncData.id,
|
||||||
|
syncData && syncData.rev,
|
||||||
|
)
|
||||||
|
.then(res => ({
|
||||||
|
// Build sync data
|
||||||
|
id: res.id,
|
||||||
|
itemId: item.id,
|
||||||
|
type: item.type,
|
||||||
|
hash: item.hash,
|
||||||
|
rev: res.rev,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
removeItem(syncData) {
|
||||||
|
const syncToken = store.getters['workspace/syncToken'];
|
||||||
|
return couchdbHelper.removeDocument(syncToken, syncData.id, syncData.rev);
|
||||||
|
},
|
||||||
|
downloadContent(token, syncLocation) {
|
||||||
|
return this.downloadData(`${syncLocation.fileId}/content`);
|
||||||
|
},
|
||||||
|
downloadData(dataId) {
|
||||||
|
const syncData = store.getters['data/syncDataByItemId'][dataId];
|
||||||
|
if (!syncData) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
const syncToken = store.getters['workspace/syncToken'];
|
||||||
|
return couchdbHelper.retrieveDocumentWithAttachments(syncToken, syncData.id)
|
||||||
|
.then((body) => {
|
||||||
|
let item;
|
||||||
|
if (body.item.type === 'content') {
|
||||||
|
item = providerUtils.parseContent(body.attachments.data, body.item.id);
|
||||||
|
} else {
|
||||||
|
item = JSON.parse(body.attachments.data);
|
||||||
|
}
|
||||||
|
const rev = body._rev; // eslint-disable-line no-underscore-dangle
|
||||||
|
if (item.hash !== syncData.hash || rev !== syncData.rev) {
|
||||||
|
store.dispatch('data/patchSyncData', {
|
||||||
|
[syncData.id]: {
|
||||||
|
...syncData,
|
||||||
|
hash: item.hash,
|
||||||
|
rev,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
uploadContent(token, content, syncLocation) {
|
||||||
|
return this.uploadData(content, `${syncLocation.fileId}/content`)
|
||||||
|
.then(() => syncLocation);
|
||||||
|
},
|
||||||
|
uploadData(item, dataId) {
|
||||||
|
const syncData = store.getters['data/syncDataByItemId'][dataId];
|
||||||
|
if (syncData && syncData.hash === item.hash) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
let data;
|
||||||
|
let dataType;
|
||||||
|
if (item.type === 'content') {
|
||||||
|
data = providerUtils.serializeContent(item);
|
||||||
|
dataType = 'text/plain';
|
||||||
|
} else {
|
||||||
|
data = JSON.stringify(item);
|
||||||
|
dataType = 'application/json';
|
||||||
|
}
|
||||||
|
const syncToken = store.getters['workspace/syncToken'];
|
||||||
|
return couchdbHelper.uploadDocument(
|
||||||
|
syncToken,
|
||||||
|
{
|
||||||
|
id: item.id,
|
||||||
|
type: item.type,
|
||||||
|
hash: item.hash,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
dataType,
|
||||||
|
syncData && syncData.id,
|
||||||
|
syncData && syncData.rev,
|
||||||
|
)
|
||||||
|
.then(res => store.dispatch('data/patchSyncData', {
|
||||||
|
[res.id]: {
|
||||||
|
// Build sync data
|
||||||
|
id: res.id,
|
||||||
|
itemId: item.id,
|
||||||
|
type: item.type,
|
||||||
|
hash: item.hash,
|
||||||
|
rev: res.rev,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
listRevisions(token, fileId) {
|
||||||
|
const syncData = store.getters['data/syncDataByItemId'][`${fileId}/content`];
|
||||||
|
if (!syncData) {
|
||||||
|
return Promise.reject(); // No need for a proper error message.
|
||||||
|
}
|
||||||
|
return couchdbHelper.retrieveDocumentWithRevisions(token, syncData.id)
|
||||||
|
.then((body) => {
|
||||||
|
const revisions = [];
|
||||||
|
body._revs_info.forEach((revInfo) => { // eslint-disable-line no-underscore-dangle
|
||||||
|
if (revInfo.status === 'available') {
|
||||||
|
revisions.push({
|
||||||
|
id: revInfo.rev,
|
||||||
|
sub: null,
|
||||||
|
created: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return revisions;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadRevision(token, fileId, revision) {
|
||||||
|
const syncData = store.getters['data/syncDataByItemId'][`${fileId}/content`];
|
||||||
|
if (!syncData) {
|
||||||
|
return Promise.reject(); // No need for a proper error message.
|
||||||
|
}
|
||||||
|
return couchdbHelper.retrieveDocument(token, syncData.id, revision.id)
|
||||||
|
.then((body) => {
|
||||||
|
revision.sub = body.sub;
|
||||||
|
revision.created = body.created;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getRevisionContent(token, fileId, revisionId) {
|
||||||
|
const syncData = store.getters['data/syncDataByItemId'][`${fileId}/content`];
|
||||||
|
if (!syncData) {
|
||||||
|
return Promise.reject(); // No need for a proper error message.
|
||||||
|
}
|
||||||
|
return couchdbHelper.retrieveDocumentWithAttachments(token, syncData.id, revisionId)
|
||||||
|
.then(body => providerUtils.parseContent(body.attachments.data, body.item.id));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
@ -40,7 +40,7 @@ export default providerRegistry.register({
|
|||||||
makePathRelative(token, syncLocation.path),
|
makePathRelative(token, syncLocation.path),
|
||||||
syncLocation.dropboxFileId,
|
syncLocation.dropboxFileId,
|
||||||
)
|
)
|
||||||
.then(({ content }) => providerUtils.parseContent(content, syncLocation));
|
.then(({ content }) => providerUtils.parseContent(content, `${syncLocation.fileId}/content`));
|
||||||
},
|
},
|
||||||
uploadContent(token, content, syncLocation) {
|
uploadContent(token, content, syncLocation) {
|
||||||
return dropboxHelper.uploadFile(
|
return dropboxHelper.uploadFile(
|
||||||
|
@ -18,7 +18,7 @@ export default providerRegistry.register({
|
|||||||
},
|
},
|
||||||
downloadContent(token, syncLocation) {
|
downloadContent(token, syncLocation) {
|
||||||
return githubHelper.downloadGist(token, syncLocation.gistId, syncLocation.filename)
|
return githubHelper.downloadGist(token, syncLocation.gistId, syncLocation.filename)
|
||||||
.then(content => providerUtils.parseContent(content, syncLocation));
|
.then(content => providerUtils.parseContent(content, `${syncLocation.fileId}/content`));
|
||||||
},
|
},
|
||||||
uploadContent(token, content, syncLocation) {
|
uploadContent(token, content, syncLocation) {
|
||||||
const file = store.state.file.itemMap[syncLocation.fileId];
|
const file = store.state.file.itemMap[syncLocation.fileId];
|
||||||
|
@ -24,7 +24,7 @@ export default providerRegistry.register({
|
|||||||
)
|
)
|
||||||
.then(({ sha, content }) => {
|
.then(({ sha, content }) => {
|
||||||
savedSha[syncLocation.id] = sha;
|
savedSha[syncLocation.id] = sha;
|
||||||
return providerUtils.parseContent(content, syncLocation);
|
return providerUtils.parseContent(content, `${syncLocation.fileId}/content`);
|
||||||
})
|
})
|
||||||
.catch(() => null); // Ignore error, without the sha upload is going to fail anyway
|
.catch(() => null); // Ignore error, without the sha upload is going to fail anyway
|
||||||
},
|
},
|
||||||
|
@ -82,8 +82,8 @@ export default providerRegistry.register({
|
|||||||
}
|
}
|
||||||
const syncToken = store.getters['workspace/syncToken'];
|
const syncToken = store.getters['workspace/syncToken'];
|
||||||
return googleHelper.downloadAppDataFile(syncToken, syncData.id)
|
return googleHelper.downloadAppDataFile(syncToken, syncData.id)
|
||||||
.then((content) => {
|
.then((data) => {
|
||||||
const item = JSON.parse(content);
|
const item = JSON.parse(data);
|
||||||
if (item.hash !== syncData.hash) {
|
if (item.hash !== syncData.hash) {
|
||||||
store.dispatch('data/patchSyncData', {
|
store.dispatch('data/patchSyncData', {
|
||||||
[syncData.id]: {
|
[syncData.id]: {
|
||||||
@ -136,7 +136,8 @@ export default providerRegistry.register({
|
|||||||
id: revision.id,
|
id: revision.id,
|
||||||
sub: revision.lastModifyingUser && revision.lastModifyingUser.permissionId,
|
sub: revision.lastModifyingUser && revision.lastModifyingUser.permissionId,
|
||||||
created: new Date(revision.modifiedTime).getTime(),
|
created: new Date(revision.modifiedTime).getTime(),
|
||||||
})));
|
}))
|
||||||
|
.sort((revision1, revision2) => revision2.created - revision1.created));
|
||||||
},
|
},
|
||||||
getRevisionContent(token, fileId, revisionId) {
|
getRevisionContent(token, fileId, revisionId) {
|
||||||
const syncData = store.getters['data/syncDataByItemId'][`${fileId}/content`];
|
const syncData = store.getters['data/syncDataByItemId'][`${fileId}/content`];
|
||||||
|
@ -110,7 +110,7 @@ export default providerRegistry.register({
|
|||||||
},
|
},
|
||||||
downloadContent(token, syncLocation) {
|
downloadContent(token, syncLocation) {
|
||||||
return googleHelper.downloadFile(token, syncLocation.driveFileId)
|
return googleHelper.downloadFile(token, syncLocation.driveFileId)
|
||||||
.then(content => providerUtils.parseContent(content, syncLocation));
|
.then(content => providerUtils.parseContent(content, `${syncLocation.fileId}/content`));
|
||||||
},
|
},
|
||||||
uploadContent(token, content, syncLocation, ifNotTooLate) {
|
uploadContent(token, content, syncLocation, ifNotTooLate) {
|
||||||
const file = store.state.file.itemMap[syncLocation.fileId];
|
const file = store.state.file.itemMap[syncLocation.fileId];
|
||||||
|
@ -398,7 +398,7 @@ export default providerRegistry.register({
|
|||||||
}
|
}
|
||||||
return googleHelper.downloadFile(token, syncData.id)
|
return googleHelper.downloadFile(token, syncData.id)
|
||||||
.then((content) => {
|
.then((content) => {
|
||||||
const item = providerUtils.parseContent(content, syncLocation);
|
const item = providerUtils.parseContent(content, `${syncLocation.fileId}/content`);
|
||||||
if (item.hash !== contentSyncData.hash) {
|
if (item.hash !== contentSyncData.hash) {
|
||||||
store.dispatch('data/patchSyncData', {
|
store.dispatch('data/patchSyncData', {
|
||||||
[contentSyncData.id]: {
|
[contentSyncData.id]: {
|
||||||
@ -543,7 +543,8 @@ export default providerRegistry.register({
|
|||||||
id: revision.id,
|
id: revision.id,
|
||||||
sub: revision.lastModifyingUser && revision.lastModifyingUser.permissionId,
|
sub: revision.lastModifyingUser && revision.lastModifyingUser.permissionId,
|
||||||
created: new Date(revision.modifiedTime).getTime(),
|
created: new Date(revision.modifiedTime).getTime(),
|
||||||
})));
|
}))
|
||||||
|
.sort((revision1, revision2) => revision2.created - revision1.created));
|
||||||
},
|
},
|
||||||
getRevisionContent(token, fileId, revisionId) {
|
getRevisionContent(token, fileId, revisionId) {
|
||||||
const syncData = store.getters['data/syncDataByItemId'][fileId];
|
const syncData = store.getters['data/syncDataByItemId'][fileId];
|
||||||
@ -551,6 +552,6 @@ export default providerRegistry.register({
|
|||||||
return Promise.reject(); // No need for a proper error message.
|
return Promise.reject(); // No need for a proper error message.
|
||||||
}
|
}
|
||||||
return googleHelper.downloadFileRevision(token, syncData.id, revisionId)
|
return googleHelper.downloadFileRevision(token, syncData.id, revisionId)
|
||||||
.then(content => providerUtils.parseContent(content));
|
.then(content => providerUtils.parseContent(content, `${fileId}/content`));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -30,21 +30,115 @@ const request = (token, options = {}) => {
|
|||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
...options,
|
...options,
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
...options.headers || {},
|
||||||
|
},
|
||||||
url: utils.resolveUrl(baseUrl, options.path || '.'),
|
url: utils.resolveUrl(baseUrl, options.path || '.'),
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
return networkSvc.request(config)
|
return networkSvc.request(config)
|
||||||
.catch(ifUnauthorized(() => onUnauthorized()
|
.catch(ifUnauthorized(() => onUnauthorized()
|
||||||
.then(() => networkSvc.request(config))));
|
.then(() => networkSvc.request(config))))
|
||||||
|
.then(res => res.body)
|
||||||
|
.catch((err) => {
|
||||||
|
if (err.status === 409) {
|
||||||
|
throw new Error('TOO_LATE');
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getDb(token) {
|
getDb(token) {
|
||||||
return request(token)
|
return request(token);
|
||||||
.then(res => res.body);
|
|
||||||
},
|
},
|
||||||
getChanges() {
|
getChanges(token, lastSeq) {
|
||||||
|
const result = {
|
||||||
|
changes: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPage = (since = 0) => request(token, {
|
||||||
|
method: 'GET',
|
||||||
|
path: '_changes',
|
||||||
|
params: {
|
||||||
|
since,
|
||||||
|
include_docs: true,
|
||||||
|
limit: 1000,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((body) => {
|
||||||
|
result.changes = result.changes.concat(body.results);
|
||||||
|
if (body.pending) {
|
||||||
|
return getPage(body.last_seq);
|
||||||
|
}
|
||||||
|
result.lastSeq = body.last_seq;
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
return getPage(lastSeq);
|
||||||
|
},
|
||||||
|
uploadDocument(
|
||||||
|
token,
|
||||||
|
item,
|
||||||
|
data = null,
|
||||||
|
dataType = null,
|
||||||
|
documentId = null,
|
||||||
|
rev = null,
|
||||||
|
) {
|
||||||
|
const options = {
|
||||||
|
method: 'POST',
|
||||||
|
body: { item },
|
||||||
|
};
|
||||||
|
if (documentId) {
|
||||||
|
options.method = 'PUT';
|
||||||
|
options.path = documentId;
|
||||||
|
options.body._rev = rev; // eslint-disable-line no-underscore-dangle
|
||||||
|
}
|
||||||
|
if (data) {
|
||||||
|
options.body._attachments = { // eslint-disable-line no-underscore-dangle
|
||||||
|
data: {
|
||||||
|
content_type: dataType,
|
||||||
|
data: utils.encodeBase64(data),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return request(token, options);
|
||||||
|
},
|
||||||
|
removeDocument(token, documentId, rev) {
|
||||||
|
return request(token, {
|
||||||
|
method: 'DELETE',
|
||||||
|
path: documentId,
|
||||||
|
params: { rev },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
retrieveDocument(token, documentId, rev) {
|
||||||
|
return request(token, {
|
||||||
|
path: documentId,
|
||||||
|
params: { rev },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
retrieveDocumentWithAttachments(token, documentId, rev) {
|
||||||
|
return request(token, {
|
||||||
|
path: documentId,
|
||||||
|
params: { attachments: true, rev },
|
||||||
|
})
|
||||||
|
.then((body) => {
|
||||||
|
body.attachments = {};
|
||||||
|
// eslint-disable-next-line no-underscore-dangle
|
||||||
|
Object.entries(body._attachments).forEach(([name, attachment]) => {
|
||||||
|
body.attachments[name] = utils.decodeBase64(attachment.data);
|
||||||
|
});
|
||||||
|
return body;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
retrieveDocumentWithRevisions(token, documentId) {
|
||||||
|
return request(token, {
|
||||||
|
path: documentId,
|
||||||
|
params: {
|
||||||
|
revs_info: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -26,9 +26,8 @@ export default {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
parseContent(serializedContent, syncLocation = {}) {
|
parseContent(serializedContent, id) {
|
||||||
const result = utils.deepCopy(store.state.content.itemMap[`${syncLocation.fileId}/content`])
|
const result = utils.deepCopy(store.state.content.itemMap[id]) || emptyContent(id);
|
||||||
|| emptyContent();
|
|
||||||
result.text = utils.sanitizeText(serializedContent);
|
result.text = utils.sanitizeText(serializedContent);
|
||||||
result.history = [];
|
result.history = [];
|
||||||
const extractedData = dataExtractor.exec(serializedContent);
|
const extractedData = dataExtractor.exec(serializedContent);
|
||||||
|
@ -365,7 +365,7 @@ function syncFile(fileId, syncContext = new SyncContext()) {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (store.state.offline) {
|
if (store.state.offline || (err && err.message === 'TOO_LATE')) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
console.error(err); // eslint-disable-line no-console
|
console.error(err); // eslint-disable-line no-console
|
||||||
@ -583,10 +583,10 @@ function syncWorkspace() {
|
|||||||
const hash = loadedContent ? loadedContent.hash : localDbSvc.hashMap.content[contentId];
|
const hash = loadedContent ? loadedContent.hash : localDbSvc.hashMap.content[contentId];
|
||||||
const syncData = store.getters['data/syncDataByItemId'][contentId];
|
const syncData = store.getters['data/syncDataByItemId'][contentId];
|
||||||
if (
|
if (
|
||||||
// Sync if syncData does not exist and content syncing was not attempted yet
|
// Sync if content syncing was not attempted yet
|
||||||
(!syncData && !syncContext.synced[contentId]) ||
|
!syncContext.synced[contentId] &&
|
||||||
// Or if content hash and syncData hash are inconsistent
|
// And if syncData does not exist or if content hash and syncData hash are inconsistent
|
||||||
(syncData && hash !== syncData.hash)
|
(!syncData || syncData.hash !== hash)
|
||||||
) {
|
) {
|
||||||
[fileId] = contentId.split('/');
|
[fileId] = contentId.split('/');
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user