CouchDB workspace (part 3)
This commit is contained in:
parent
2374f459df
commit
f66f120afc
@ -160,6 +160,7 @@ export default {
|
|||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
const loadOne = () => {
|
const loadOne = () => {
|
||||||
|
if (!this.destroyed) {
|
||||||
this.$store.dispatch('queue/enqueue',
|
this.$store.dispatch('queue/enqueue',
|
||||||
() => {
|
() => {
|
||||||
let loadPromise;
|
let loadPromise;
|
||||||
@ -167,13 +168,15 @@ export default {
|
|||||||
if (!revision.created) {
|
if (!revision.created) {
|
||||||
const syncToken = this.$store.getters['workspace/syncToken'];
|
const syncToken = this.$store.getters['workspace/syncToken'];
|
||||||
const currentFile = this.$store.getters['file/current'];
|
const currentFile = this.$store.getters['file/current'];
|
||||||
loadPromise = this.workspaceProvider.loadRevision(syncToken, currentFile.id, revision)
|
loadPromise = this.workspaceProvider
|
||||||
|
.loadRevision(syncToken, currentFile.id, revision)
|
||||||
.then(() => loadOne());
|
.then(() => loadOne());
|
||||||
}
|
}
|
||||||
return loadPromise;
|
return loadPromise;
|
||||||
});
|
});
|
||||||
return loadPromise;
|
return loadPromise;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$watch(
|
this.$watch(
|
||||||
@ -203,7 +206,7 @@ export default {
|
|||||||
// Remove event listener
|
// Remove event listener
|
||||||
window.removeEventListener('keyup', this.onKeyup);
|
window.removeEventListener('keyup', this.onKeyup);
|
||||||
// Cancel loading revisions
|
// Cancel loading revisions
|
||||||
this.showCount = 0;
|
this.destroyed = true;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="side-bar__panel side-bar__panel--menu">
|
<div class="side-bar__panel side-bar__panel--menu">
|
||||||
<div class="workspace" v-for="(workspace, id) in workspaces" :key="id">
|
<div class="workspace" v-for="(workspace, id) in sanitizedWorkspaces" :key="id">
|
||||||
<menu-entry :href="workspace.url" target="_blank">
|
<menu-entry :href="workspace.url" target="_blank">
|
||||||
<icon-provider slot="icon" :provider-id="workspace.providerId"></icon-provider>
|
<icon-provider slot="icon" :provider-id="workspace.providerId"></icon-provider>
|
||||||
<div class="workspace__name"><div class="menu-entry__label" v-if="currentWorkspace === workspace">current</div>{{workspace.name}}</div>
|
<div class="workspace__name"><div class="menu-entry__label" v-if="currentWorkspace === workspace">current</div>{{workspace.name}}</div>
|
||||||
@ -34,7 +34,7 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters('data', [
|
...mapGetters('data', [
|
||||||
'workspaces',
|
'sanitizedWorkspaces',
|
||||||
]),
|
]),
|
||||||
...mapGetters('workspace', [
|
...mapGetters('workspace', [
|
||||||
'currentWorkspace',
|
'currentWorkspace',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<modal-inner class="modal__inner-1--workspace-management" aria-label="Manage workspaces">
|
<modal-inner class="modal__inner-1--workspace-management" aria-label="Manage workspaces">
|
||||||
<div class="modal__content">
|
<div class="modal__content">
|
||||||
<div class="workspace-entry flex flex--row flex--align-center" v-for="(workspace, id) in workspaces" :key="id">
|
<div class="workspace-entry flex flex--row flex--align-center" v-for="(workspace, id) in sanitizedWorkspaces" :key="id">
|
||||||
<div class="workspace-entry__icon flex flex--column flex--center">
|
<div class="workspace-entry__icon flex flex--column flex--center">
|
||||||
<icon-provider :provider-id="workspace.providerId"></icon-provider>
|
<icon-provider :provider-id="workspace.providerId"></icon-provider>
|
||||||
</div>
|
</div>
|
||||||
@ -49,6 +49,7 @@ export default {
|
|||||||
]),
|
]),
|
||||||
...mapGetters('data', [
|
...mapGetters('data', [
|
||||||
'workspaces',
|
'workspaces',
|
||||||
|
'sanitizedWorkspaces',
|
||||||
]),
|
]),
|
||||||
...mapGetters('workspace', [
|
...mapGetters('workspace', [
|
||||||
'mainWorkspace',
|
'mainWorkspace',
|
||||||
|
@ -11,10 +11,9 @@
|
|||||||
<b>Example:</b> https://instance.smileupps.com/stackedit-workspace
|
<b>Example:</b> https://instance.smileupps.com/stackedit-workspace
|
||||||
</div>
|
</div>
|
||||||
<div class="form-entry__actions">
|
<div class="form-entry__actions">
|
||||||
<a href="javascript:void(0)" v-if="!showInfo" @click="showInfo = true">More info</a>
|
<a href="https://community.stackedit.io/t/couchdb-workspace-setup/" target="_blank">More info</a>
|
||||||
</div>
|
</div>
|
||||||
</form-entry>
|
</form-entry>
|
||||||
<div class="couchdb-workspace__info" v-if="showInfo" v-html="info"></div>
|
|
||||||
</div>
|
</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>
|
||||||
@ -26,19 +25,11 @@
|
|||||||
<script>
|
<script>
|
||||||
import modalTemplate from '../common/modalTemplate';
|
import modalTemplate from '../common/modalTemplate';
|
||||||
import utils from '../../../services/utils';
|
import utils from '../../../services/utils';
|
||||||
import couchdbSetup from '../../../data/couchdbSetup.md';
|
|
||||||
import markdownConversionSvc from '../../../services/markdownConversionSvc';
|
|
||||||
|
|
||||||
export default modalTemplate({
|
export default modalTemplate({
|
||||||
data: () => ({
|
data: () => ({
|
||||||
dbUrl: '',
|
dbUrl: '',
|
||||||
showInfo: false,
|
|
||||||
}),
|
}),
|
||||||
computed: {
|
|
||||||
info() {
|
|
||||||
return markdownConversionSvc.defaultConverter.render(couchdbSetup);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
resolve() {
|
resolve() {
|
||||||
if (!this.dbUrl) {
|
if (!this.dbUrl) {
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
### Pre-requisites
|
|
||||||
|
|
||||||
- CouchDB 0.11 or later,
|
|
||||||
- In order to work with https://stackedit.io, your database needs to be accessible over HTTPS.
|
|
||||||
|
|
||||||
> **Tip:** [Smileupps](https://www.smileupps.com/) provide free CouchDB hosting.
|
|
||||||
|
|
||||||
### Enable CORS
|
|
||||||
|
|
||||||
Add the following key/value pairs to your CouchDB configuration:
|
|
||||||
|
|
||||||
```
|
|
||||||
[httpd]
|
|
||||||
enable_cors = true
|
|
||||||
|
|
||||||
[cors]
|
|
||||||
origins = https://stackedit.io
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Create the database
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X PUT https://instance.smileupps.com/stackedit-workspace
|
|
||||||
```
|
|
||||||
|
|
||||||
> You may want to restrict access to the database by [specifying members](http://docs.couchdb.org/en/latest/api/database/security.html).
|
|
@ -4,6 +4,13 @@ import providerRegistry from './providerRegistry';
|
|||||||
import providerUtils from './providerUtils';
|
import providerUtils from './providerUtils';
|
||||||
import utils from '../utils';
|
import utils from '../utils';
|
||||||
|
|
||||||
|
const getSyncData = (fileId) => {
|
||||||
|
const syncData = store.getters['data/syncDataByItemId'][`${fileId}/content`];
|
||||||
|
return syncData
|
||||||
|
? Promise.resolve(syncData)
|
||||||
|
: Promise.reject(); // No need for a proper error message.
|
||||||
|
};
|
||||||
|
|
||||||
export default providerRegistry.register({
|
export default providerRegistry.register({
|
||||||
id: 'couchdbWorkspace',
|
id: 'couchdbWorkspace',
|
||||||
getToken() {
|
getToken() {
|
||||||
@ -184,11 +191,8 @@ export default providerRegistry.register({
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
listRevisions(token, fileId) {
|
listRevisions(token, fileId) {
|
||||||
const syncData = store.getters['data/syncDataByItemId'][`${fileId}/content`];
|
return getSyncData(fileId)
|
||||||
if (!syncData) {
|
.then(syncData => couchdbHelper.retrieveDocumentWithRevisions(token, syncData.id))
|
||||||
return Promise.reject(); // No need for a proper error message.
|
|
||||||
}
|
|
||||||
return couchdbHelper.retrieveDocumentWithRevisions(token, syncData.id)
|
|
||||||
.then((body) => {
|
.then((body) => {
|
||||||
const revisions = [];
|
const revisions = [];
|
||||||
body._revs_info.forEach((revInfo) => { // eslint-disable-line no-underscore-dangle
|
body._revs_info.forEach((revInfo) => { // eslint-disable-line no-underscore-dangle
|
||||||
@ -204,22 +208,17 @@ export default providerRegistry.register({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
loadRevision(token, fileId, revision) {
|
loadRevision(token, fileId, revision) {
|
||||||
const syncData = store.getters['data/syncDataByItemId'][`${fileId}/content`];
|
return getSyncData(fileId)
|
||||||
if (!syncData) {
|
.then(syncData => couchdbHelper.retrieveDocument(token, syncData.id, revision.id))
|
||||||
return Promise.reject(); // No need for a proper error message.
|
|
||||||
}
|
|
||||||
return couchdbHelper.retrieveDocument(token, syncData.id, revision.id)
|
|
||||||
.then((body) => {
|
.then((body) => {
|
||||||
revision.sub = body.sub;
|
revision.sub = body.sub;
|
||||||
revision.created = body.created;
|
revision.created = body.time || 1; // Has to be truthy to prevent from loading several times
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getRevisionContent(token, fileId, revisionId) {
|
getRevisionContent(token, fileId, revisionId) {
|
||||||
const syncData = store.getters['data/syncDataByItemId'][`${fileId}/content`];
|
return getSyncData(fileId)
|
||||||
if (!syncData) {
|
.then(syncData => couchdbHelper
|
||||||
return Promise.reject(); // No need for a proper error message.
|
.retrieveDocumentWithAttachments(token, syncData.id, revisionId))
|
||||||
}
|
|
||||||
return couchdbHelper.retrieveDocumentWithAttachments(token, syncData.id, revisionId)
|
|
||||||
.then(body => providerUtils.parseContent(body.attachments.data, body.item.id));
|
.then(body => providerUtils.parseContent(body.attachments.data, body.item.id));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -6,6 +6,13 @@ import utils from '../utils';
|
|||||||
|
|
||||||
let fileIdToOpen;
|
let fileIdToOpen;
|
||||||
|
|
||||||
|
const getSyncData = (fileId) => {
|
||||||
|
const syncData = store.getters['data/syncDataByItemId'][`${fileId}/content`];
|
||||||
|
return syncData
|
||||||
|
? Promise.resolve(syncData)
|
||||||
|
: Promise.reject(); // No need for a proper error message.
|
||||||
|
};
|
||||||
|
|
||||||
export default providerRegistry.register({
|
export default providerRegistry.register({
|
||||||
id: 'googleDriveWorkspace',
|
id: 'googleDriveWorkspace',
|
||||||
getToken() {
|
getToken() {
|
||||||
@ -441,7 +448,7 @@ export default providerRegistry.register({
|
|||||||
uploadContent(token, content, syncLocation, ifNotTooLate) {
|
uploadContent(token, content, syncLocation, ifNotTooLate) {
|
||||||
const contentSyncData = store.getters['data/syncDataByItemId'][`${syncLocation.fileId}/content`];
|
const contentSyncData = store.getters['data/syncDataByItemId'][`${syncLocation.fileId}/content`];
|
||||||
if (contentSyncData && contentSyncData.hash === content.hash) {
|
if (contentSyncData && contentSyncData.hash === content.hash) {
|
||||||
return Promise.resolve();
|
return Promise.resolve(syncLocation);
|
||||||
}
|
}
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@ -534,11 +541,8 @@ export default providerRegistry.register({
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
listRevisions(token, fileId) {
|
listRevisions(token, fileId) {
|
||||||
const syncData = store.getters['data/syncDataByItemId'][fileId];
|
return getSyncData(fileId)
|
||||||
if (!syncData) {
|
.then(syncData => googleHelper.getFileRevisions(token, syncData.id))
|
||||||
return Promise.reject(); // No need for a proper error message.
|
|
||||||
}
|
|
||||||
return googleHelper.getFileRevisions(token, syncData.id)
|
|
||||||
.then(revisions => revisions.map(revision => ({
|
.then(revisions => revisions.map(revision => ({
|
||||||
id: revision.id,
|
id: revision.id,
|
||||||
sub: revision.lastModifyingUser && revision.lastModifyingUser.permissionId,
|
sub: revision.lastModifyingUser && revision.lastModifyingUser.permissionId,
|
||||||
@ -547,11 +551,8 @@ export default providerRegistry.register({
|
|||||||
.sort((revision1, revision2) => revision2.created - revision1.created));
|
.sort((revision1, revision2) => revision2.created - revision1.created));
|
||||||
},
|
},
|
||||||
getRevisionContent(token, fileId, revisionId) {
|
getRevisionContent(token, fileId, revisionId) {
|
||||||
const syncData = store.getters['data/syncDataByItemId'][fileId];
|
return getSyncData(fileId)
|
||||||
if (!syncData) {
|
.then(syncData => googleHelper.downloadFileRevision(token, syncData.id, revisionId))
|
||||||
return Promise.reject(); // No need for a proper error message.
|
|
||||||
}
|
|
||||||
return googleHelper.downloadFileRevision(token, syncData.id, revisionId)
|
|
||||||
.then(content => providerUtils.parseContent(content, `${fileId}/content`));
|
.then(content => providerUtils.parseContent(content, `${fileId}/content`));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -89,8 +89,12 @@ export default {
|
|||||||
) {
|
) {
|
||||||
const options = {
|
const options = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: { item },
|
body: { item, time: Date.now() },
|
||||||
};
|
};
|
||||||
|
const loginToken = store.getters['workspace/loginToken'];
|
||||||
|
if (loginToken) {
|
||||||
|
options.body.sub = loginToken.sub;
|
||||||
|
}
|
||||||
if (documentId) {
|
if (documentId) {
|
||||||
options.method = 'PUT';
|
options.method = 'PUT';
|
||||||
options.path = documentId;
|
options.path = documentId;
|
||||||
|
Loading…
Reference in New Issue
Block a user