Github workspace (part 2)
This commit is contained in:
parent
53ccee0d84
commit
b896a2e086
12
package-lock.json
generated
12
package-lock.json
generated
@ -14498,9 +14498,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"vue": {
|
"vue": {
|
||||||
"version": "2.5.13",
|
"version": "2.5.16",
|
||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.5.13.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-2.5.16.tgz",
|
||||||
"integrity": "sha512-3D+lY7HTkKbtswDM4BBHgqyq+qo8IAEE8lz8va1dz3LLmttjgo0FxairO4r1iN2OBqk8o1FyL4hvzzTFEdQSEw=="
|
"integrity": "sha512-/ffmsiVuPC8PsWcFkZngdpas19ABm5mh2wA7iDqcltyCTwlgZjHGeJYOXkBMo422iPwIcviOtrTCUpSfXmToLQ=="
|
||||||
},
|
},
|
||||||
"vue-hot-reload-api": {
|
"vue-hot-reload-api": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.4",
|
||||||
@ -14588,9 +14588,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"vuex": {
|
"vuex": {
|
||||||
"version": "2.5.0",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/vuex/-/vuex-2.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.0.1.tgz",
|
||||||
"integrity": "sha512-5oJPOJySBgSgSzoeO+gZB/BbN/XsapgIF6tz34UwJqnGZMQurzIO3B4KIBf862gfc9ya+oduY5sSkq+5/oOilQ=="
|
"integrity": "sha512-wLoqz0B7DSZtgbWL1ShIBBCjv22GV5U+vcBFox658g6V0s4wZV9P4YjCNyoHSyIBpj1f29JBoNQIqD82cR4O3w=="
|
||||||
},
|
},
|
||||||
"w3c-hr-time": {
|
"w3c-hr-time": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
|
@ -52,8 +52,8 @@
|
|||||||
"serve-static": "^1.12.6",
|
"serve-static": "^1.12.6",
|
||||||
"tmp": "^0.0.33",
|
"tmp": "^0.0.33",
|
||||||
"turndown": "^4.0.1",
|
"turndown": "^4.0.1",
|
||||||
"vue": "^2.3.3",
|
"vue": "^2.5.16",
|
||||||
"vuex": "^2.3.1"
|
"vuex": "^3.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^6.7.2",
|
"autoprefixer": "^6.7.2",
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { mapState, mapGetters, mapActions } from 'vuex';
|
import { mapState, mapGetters, mapActions } from 'vuex';
|
||||||
import ExplorerNode from './ExplorerNode';
|
import ExplorerNode from './ExplorerNode';
|
||||||
|
import explorerSvc from '../services/explorerSvc';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -49,10 +50,8 @@ export default {
|
|||||||
...mapActions('data', [
|
...mapActions('data', [
|
||||||
'toggleExplorer',
|
'toggleExplorer',
|
||||||
]),
|
]),
|
||||||
...mapActions('explorer', [
|
newItem: isFolder => explorerSvc.newItem(isFolder),
|
||||||
'newItem',
|
deleteItem: () => explorerSvc.deleteItem(),
|
||||||
'deleteItem',
|
|
||||||
]),
|
|
||||||
editItem() {
|
editItem() {
|
||||||
const node = this.selectedNode;
|
const node = this.selectedNode;
|
||||||
if (!node.isTrash && !node.isTemp) {
|
if (!node.isTrash && !node.isTemp) {
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapMutations, mapActions } from 'vuex';
|
import { mapMutations, mapActions } from 'vuex';
|
||||||
import utils from '../services/utils';
|
import fileSvc from '../services/fileSvc';
|
||||||
|
import explorerSvc from '../services/explorerSvc';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'explorer-node', // Required for recursivity
|
name: 'explorer-node', // Required for recursivity
|
||||||
@ -77,8 +78,6 @@ export default {
|
|||||||
]),
|
]),
|
||||||
...mapActions('explorer', [
|
...mapActions('explorer', [
|
||||||
'setDragTarget',
|
'setDragTarget',
|
||||||
'newItem',
|
|
||||||
'deleteItem',
|
|
||||||
]),
|
]),
|
||||||
select(id = this.node.item.id, doOpen = true) {
|
select(id = this.node.item.id, doOpen = true) {
|
||||||
const node = this.$store.getters['explorer/nodeMap'][id];
|
const node = this.$store.getters['explorer/nodeMap'][id];
|
||||||
@ -102,31 +101,26 @@ export default {
|
|||||||
const newChildNode = this.$store.state.explorer.newChildNode;
|
const newChildNode = this.$store.state.explorer.newChildNode;
|
||||||
if (!cancel && !newChildNode.isNil && newChildNode.item.name) {
|
if (!cancel && !newChildNode.isNil && newChildNode.item.name) {
|
||||||
if (newChildNode.isFolder) {
|
if (newChildNode.isFolder) {
|
||||||
const id = utils.uid();
|
fileSvc.storeItem(newChildNode.item)
|
||||||
this.$store.commit('folder/setItem', {
|
.then(item => this.select(item.id), () => { /* cancel */ });
|
||||||
...newChildNode.item,
|
|
||||||
id,
|
|
||||||
name: utils.sanitizeName(newChildNode.item.name),
|
|
||||||
});
|
|
||||||
this.select(id);
|
|
||||||
} else {
|
} else {
|
||||||
this.$store.dispatch('createFile', newChildNode.item)
|
fileSvc.createFile(newChildNode.item)
|
||||||
.then(file => this.select(file.id));
|
.then(item => this.select(item.id), () => { /* cancel */ });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.$store.commit('explorer/setNewItem', null);
|
this.$store.commit('explorer/setNewItem', null);
|
||||||
},
|
},
|
||||||
submitEdit(cancel) {
|
submitEdit(cancel) {
|
||||||
const editingNode = this.$store.getters['explorer/editingNode'];
|
const item = this.$store.getters['explorer/editingNode'].item;
|
||||||
const id = editingNode.item.id;
|
|
||||||
const value = this.editingValue;
|
const value = this.editingValue;
|
||||||
if (!cancel && id && value) {
|
|
||||||
this.$store.commit(editingNode.isFolder ? 'folder/patchItem' : 'file/patchItem', {
|
|
||||||
id,
|
|
||||||
name: utils.sanitizeName(value),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.setEditingId(null);
|
this.setEditingId(null);
|
||||||
|
if (!cancel && item.id && value) {
|
||||||
|
fileSvc.storeItem({
|
||||||
|
...item,
|
||||||
|
name: value,
|
||||||
|
})
|
||||||
|
.catch(() => { /* cancel */ });
|
||||||
|
}
|
||||||
},
|
},
|
||||||
setDragSourceId(evt) {
|
setDragSourceId(evt) {
|
||||||
if (this.node.noDrag) {
|
if (this.node.noDrag) {
|
||||||
@ -169,11 +163,11 @@ export default {
|
|||||||
items: [{
|
items: [{
|
||||||
name: 'New file',
|
name: 'New file',
|
||||||
disabled: !this.node.isFolder || this.node.isTrash,
|
disabled: !this.node.isFolder || this.node.isTrash,
|
||||||
perform: () => this.newItem(false),
|
perform: () => explorerSvc.newItem(false),
|
||||||
}, {
|
}, {
|
||||||
name: 'New folder',
|
name: 'New folder',
|
||||||
disabled: !this.node.isFolder || this.node.isTrash || this.node.isTemp,
|
disabled: !this.node.isFolder || this.node.isTrash || this.node.isTemp,
|
||||||
perform: () => this.newItem(true),
|
perform: () => explorerSvc.newItem(true),
|
||||||
}, {
|
}, {
|
||||||
type: 'separator',
|
type: 'separator',
|
||||||
}, {
|
}, {
|
||||||
@ -182,7 +176,7 @@ export default {
|
|||||||
perform: () => this.setEditingId(this.node.item.id),
|
perform: () => this.setEditingId(this.node.item.id),
|
||||||
}, {
|
}, {
|
||||||
name: 'Delete',
|
name: 'Delete',
|
||||||
perform: () => this.deleteItem(),
|
perform: () => explorerSvc.deleteItem(),
|
||||||
}],
|
}],
|
||||||
})
|
})
|
||||||
.then(item => item.perform());
|
.then(item => item.perform());
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<div class="navigation-bar__title navigation-bar__title--fake text-input"></div>
|
<div class="navigation-bar__title navigation-bar__title--fake text-input"></div>
|
||||||
<div class="navigation-bar__title navigation-bar__title--text text-input" :style="{width: titleWidth + 'px'}">{{title}}</div>
|
<div class="navigation-bar__title navigation-bar__title--text text-input" :style="{width: titleWidth + 'px'}">{{title}}</div>
|
||||||
<input class="navigation-bar__title navigation-bar__title--input text-input" :class="{'navigation-bar__title--focus': titleFocus, 'navigation-bar__title--scrolling': titleScrolling}" :style="{width: titleWidth + 'px'}" @focus="editTitle(true)" @blur="editTitle(false)" @keydown.enter="submitTitle()" @keydown.esc="submitTitle(true)" @mouseenter="titleHover = true" @mouseleave="titleHover = false" v-model="title">
|
<input class="navigation-bar__title navigation-bar__title--input text-input" :class="{'navigation-bar__title--focus': titleFocus, 'navigation-bar__title--scrolling': titleScrolling}" :style="{width: titleWidth + 'px'}" @focus="editTitle(true)" @blur="editTitle(false)" @keydown.enter="submitTitle(false)" @keydown.esc="submitTitle(true)" @mouseenter="titleHover = true" @mouseleave="titleHover = false" v-model="title">
|
||||||
<!-- Sync/Publish -->
|
<!-- Sync/Publish -->
|
||||||
<div class="flex flex--row" :class="{'navigation-bar__hidden': styles.hideLocations}">
|
<div class="flex flex--row" :class="{'navigation-bar__hidden': styles.hideLocations}">
|
||||||
<a class="navigation-bar__button navigation-bar__button--location button" :class="{'navigation-bar__button--blink': location.id === currentLocation.id}" v-for="location in syncLocations" :key="location.id" :href="location.url" target="_blank" v-title="'Synchronized location'"><icon-provider :provider-id="location.providerId"></icon-provider></a>
|
<a class="navigation-bar__button navigation-bar__button--location button" :class="{'navigation-bar__button--blink': location.id === currentLocation.id}" v-for="location in syncLocations" :key="location.id" :href="location.url" target="_blank" v-title="'Synchronized location'"><icon-provider :provider-id="location.providerId"></icon-provider></a>
|
||||||
@ -56,6 +56,7 @@ import tempFileSvc from '../services/tempFileSvc';
|
|||||||
import utils from '../services/utils';
|
import utils from '../services/utils';
|
||||||
import pagedownButtons from '../data/pagedownButtons';
|
import pagedownButtons from '../data/pagedownButtons';
|
||||||
import store from '../store';
|
import store from '../store';
|
||||||
|
import fileSvc from '../services/fileSvc';
|
||||||
|
|
||||||
// According to mousetrap
|
// According to mousetrap
|
||||||
const mod = /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? 'Meta' : 'Ctrl';
|
const mod = /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? 'Meta' : 'Ctrl';
|
||||||
@ -151,6 +152,13 @@ export default {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
editCancelTrigger() {
|
||||||
|
const current = this.$store.getters['file/current'];
|
||||||
|
return utils.serializeObject([
|
||||||
|
current.id,
|
||||||
|
current.name,
|
||||||
|
]);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations('content', [
|
...mapMutations('content', [
|
||||||
@ -190,10 +198,13 @@ export default {
|
|||||||
this.titleInputElt.setSelectionRange(0, this.titleInputElt.value.length);
|
this.titleInputElt.setSelectionRange(0, this.titleInputElt.value.length);
|
||||||
} else {
|
} else {
|
||||||
const title = this.title.trim();
|
const title = this.title.trim();
|
||||||
if (title) {
|
|
||||||
this.$store.dispatch('file/patchCurrent', { name: utils.sanitizeName(title) });
|
|
||||||
} else {
|
|
||||||
this.title = this.$store.getters['file/current'].name;
|
this.title = this.$store.getters['file/current'].name;
|
||||||
|
if (title) {
|
||||||
|
fileSvc.storeItem({
|
||||||
|
...this.$store.getters['file/current'],
|
||||||
|
name: title,
|
||||||
|
})
|
||||||
|
.catch(() => { /* Cancel */ });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -209,9 +220,10 @@ export default {
|
|||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.$watch(
|
this.$watch(
|
||||||
() => this.$store.getters['file/current'].name,
|
() => this.editCancelTrigger,
|
||||||
(name) => {
|
() => {
|
||||||
this.title = name;
|
this.title = '';
|
||||||
|
this.editTitle(false);
|
||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import userSvc from '../services/userSvc';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['providerId'],
|
props: ['providerId'],
|
||||||
computed: {
|
computed: {
|
||||||
@ -15,8 +13,5 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
|
||||||
userSvc.getInfo(this.userId);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -51,7 +51,7 @@ export default {
|
|||||||
this.$store.dispatch('modal/commentDeletion')
|
this.$store.dispatch('modal/commentDeletion')
|
||||||
.then(
|
.then(
|
||||||
() => this.$store.dispatch('discussion/cleanCurrentFile', { filterComment: this.comment }),
|
() => this.$store.dispatch('discussion/cleanCurrentFile', { filterComment: this.comment }),
|
||||||
() => {}); // Cancel
|
() => { /* Cancel */ });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -99,7 +99,7 @@ export default {
|
|||||||
() => this.$store.dispatch('discussion/cleanCurrentFile', {
|
() => this.$store.dispatch('discussion/cleanCurrentFile', {
|
||||||
filterDiscussion: this.currentDiscussion,
|
filterDiscussion: this.currentDiscussion,
|
||||||
}),
|
}),
|
||||||
() => {}); // Cancel
|
() => { /* Cancel */ });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<div class="comment__header flex flex--row flex--space-between flex--align-center">
|
<div class="comment__header flex flex--row flex--space-between flex--align-center">
|
||||||
<div class="comment__user flex flex--row flex--align-center">
|
<div class="comment__user flex flex--row flex--align-center">
|
||||||
<div class="comment__user-image">
|
<div class="comment__user-image">
|
||||||
<user-image :user-id="loginToken.sub"></user-image>
|
<user-image :user-id="userId"></user-image>
|
||||||
</div>
|
</div>
|
||||||
<span class="user-name">{{loginToken.name}}</span>
|
<span class="user-name">{{loginToken.name}}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -33,9 +33,12 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
UserImage,
|
UserImage,
|
||||||
},
|
},
|
||||||
computed: mapGetters('workspace', [
|
computed: {
|
||||||
|
...mapGetters('workspace', [
|
||||||
'loginToken',
|
'loginToken',
|
||||||
|
'userId',
|
||||||
]),
|
]),
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations('discussion', [
|
...mapMutations('discussion', [
|
||||||
'setNewCommentFocus',
|
'setNewCommentFocus',
|
||||||
@ -53,7 +56,7 @@ export default {
|
|||||||
const discussionId = this.$store.state.discussion.currentDiscussionId;
|
const discussionId = this.$store.state.discussion.currentDiscussionId;
|
||||||
const comment = {
|
const comment = {
|
||||||
discussionId,
|
discussionId,
|
||||||
sub: this.loginToken.sub,
|
sub: this.userId,
|
||||||
text,
|
text,
|
||||||
created: Date.now(),
|
created: Date.now(),
|
||||||
};
|
};
|
||||||
|
@ -35,19 +35,19 @@ export default {
|
|||||||
exportMarkdown() {
|
exportMarkdown() {
|
||||||
const currentFile = this.$store.getters['file/current'];
|
const currentFile = this.$store.getters['file/current'];
|
||||||
return exportSvc.exportToDisk(currentFile.id, 'md')
|
return exportSvc.exportToDisk(currentFile.id, 'md')
|
||||||
.catch(() => {}); // Cancel
|
.catch(() => { /* Cancel */ });
|
||||||
},
|
},
|
||||||
exportHtml() {
|
exportHtml() {
|
||||||
return this.$store.dispatch('modal/open', 'htmlExport')
|
return this.$store.dispatch('modal/open', 'htmlExport')
|
||||||
.catch(() => {}); // Cancel
|
.catch(() => { /* Cancel */ });
|
||||||
},
|
},
|
||||||
exportPdf() {
|
exportPdf() {
|
||||||
return this.$store.dispatch('modal/open', 'pdfExport')
|
return this.$store.dispatch('modal/open', 'pdfExport')
|
||||||
.catch(() => {}); // Cancel
|
.catch(() => { /* Cancel */ });
|
||||||
},
|
},
|
||||||
exportPandoc() {
|
exportPandoc() {
|
||||||
return this.$store.dispatch('modal/open', 'pandocExport')
|
return this.$store.dispatch('modal/open', 'pandocExport')
|
||||||
.catch(() => {}); // Cancel
|
.catch(() => { /* Cancel */ });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -100,7 +100,7 @@ export default {
|
|||||||
return googleHelper.signin()
|
return googleHelper.signin()
|
||||||
.then(
|
.then(
|
||||||
() => syncSvc.requestSync(),
|
() => syncSvc.requestSync(),
|
||||||
() => {}, // Cancel
|
() => { /* Cancel */ },
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
|
@ -29,6 +29,7 @@ import htmlSanitizer from '../../libs/htmlSanitizer';
|
|||||||
import MenuEntry from './common/MenuEntry';
|
import MenuEntry from './common/MenuEntry';
|
||||||
import Provider from '../../services/providers/common/Provider';
|
import Provider from '../../services/providers/common/Provider';
|
||||||
import store from '../../store';
|
import store from '../../store';
|
||||||
|
import fileSvc from '../../services/fileSvc';
|
||||||
|
|
||||||
const turndownService = new TurndownService(store.getters['data/computedSettings'].turndown);
|
const turndownService = new TurndownService(store.getters['data/computedSettings'].turndown);
|
||||||
|
|
||||||
@ -55,16 +56,18 @@ export default {
|
|||||||
onImportMarkdown(evt) {
|
onImportMarkdown(evt) {
|
||||||
const file = evt.target.files[0];
|
const file = evt.target.files[0];
|
||||||
readFile(file)
|
readFile(file)
|
||||||
.then(content => this.$store.dispatch('createFile', {
|
.then(content => fileSvc.createFile({
|
||||||
...Provider.parseContent(content),
|
...Provider.parseContent(content),
|
||||||
name: file.name,
|
name: file.name,
|
||||||
})
|
})
|
||||||
.then(item => this.$store.commit('file/setCurrentId', item.id)));
|
.then(
|
||||||
|
item => this.$store.commit('file/setCurrentId', item.id)),
|
||||||
|
() => { /* Cancel */ });
|
||||||
},
|
},
|
||||||
onImportHtml(evt) {
|
onImportHtml(evt) {
|
||||||
const file = evt.target.files[0];
|
const file = evt.target.files[0];
|
||||||
readFile(file)
|
readFile(file)
|
||||||
.then(content => this.$store.dispatch('createFile', {
|
.then(content => fileSvc.createFile({
|
||||||
...Provider.parseContent(
|
...Provider.parseContent(
|
||||||
turndownService.turndown(
|
turndownService.turndown(
|
||||||
htmlSanitizer.sanitizeHtml(content)
|
htmlSanitizer.sanitizeHtml(content)
|
||||||
@ -72,7 +75,9 @@ export default {
|
|||||||
)),
|
)),
|
||||||
name: file.name,
|
name: file.name,
|
||||||
}))
|
}))
|
||||||
.then(item => this.$store.commit('file/setCurrentId', item.id));
|
.then(
|
||||||
|
item => this.$store.commit('file/setCurrentId', item.id),
|
||||||
|
() => { /* Cancel */ });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<div class="menu-info-entries">
|
<div class="menu-info-entries">
|
||||||
<div class="menu-entry menu-entry--info flex flex--row flex--align-center" v-if="loginToken">
|
<div class="menu-entry menu-entry--info flex flex--row flex--align-center" v-if="loginToken">
|
||||||
<div class="menu-entry__icon menu-entry__icon--image">
|
<div class="menu-entry__icon menu-entry__icon--image">
|
||||||
<user-image :user-id="loginToken.sub"></user-image>
|
<user-image :user-id="userId"></user-image>
|
||||||
</div>
|
</div>
|
||||||
<span>Signed in as <b>{{loginToken.name}}</b>.</span>
|
<span>Signed in as <b>{{loginToken.name}}</b>.</span>
|
||||||
</div>
|
</div>
|
||||||
@ -97,6 +97,7 @@ export default {
|
|||||||
'currentWorkspace',
|
'currentWorkspace',
|
||||||
'syncToken',
|
'syncToken',
|
||||||
'loginToken',
|
'loginToken',
|
||||||
|
'userId',
|
||||||
]),
|
]),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -107,12 +108,12 @@ export default {
|
|||||||
return googleHelper.signin()
|
return googleHelper.signin()
|
||||||
.then(
|
.then(
|
||||||
() => syncSvc.requestSync(),
|
() => syncSvc.requestSync(),
|
||||||
() => {}, // Cancel
|
() => { /* Cancel */ },
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
fileProperties() {
|
fileProperties() {
|
||||||
return this.$store.dispatch('modal/open', 'fileProperties')
|
return this.$store.dispatch('modal/open', 'fileProperties')
|
||||||
.catch(() => {}); // Cancel
|
.catch(() => { /* Cancel */ });
|
||||||
},
|
},
|
||||||
print() {
|
print() {
|
||||||
print();
|
print();
|
||||||
|
@ -82,14 +82,14 @@ export default {
|
|||||||
return this.$store.dispatch('modal/open', 'settings')
|
return this.$store.dispatch('modal/open', 'settings')
|
||||||
.then(
|
.then(
|
||||||
settings => this.$store.dispatch('data/setSettings', settings),
|
settings => this.$store.dispatch('data/setSettings', settings),
|
||||||
() => {}, // Cancel
|
() => { /* Cancel */ },
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
templates() {
|
templates() {
|
||||||
return this.$store.dispatch('modal/open', 'templates')
|
return this.$store.dispatch('modal/open', 'templates')
|
||||||
.then(
|
.then(
|
||||||
({ templates }) => this.$store.dispatch('data/setTemplates', templates),
|
({ templates }) => this.$store.dispatch('data/setTemplates', templates),
|
||||||
() => {}, // Cancel
|
() => { /* Cancel */ },
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
reset() {
|
reset() {
|
||||||
|
@ -123,6 +123,8 @@ const openPublishModal = (token, type) => store.dispatch('modal/open', {
|
|||||||
token,
|
token,
|
||||||
}).then(publishLocation => publishSvc.createPublishLocation(publishLocation));
|
}).then(publishLocation => publishSvc.createPublishLocation(publishLocation));
|
||||||
|
|
||||||
|
const onCancel = () => {};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
MenuEntry,
|
MenuEntry,
|
||||||
@ -181,68 +183,68 @@ export default {
|
|||||||
type: 'googleDriveAccount',
|
type: 'googleDriveAccount',
|
||||||
onResolve: () => googleHelper.addDriveAccount(!store.getters['data/localSettings'].googleDriveRestrictedAccess),
|
onResolve: () => googleHelper.addDriveAccount(!store.getters['data/localSettings'].googleDriveRestrictedAccess),
|
||||||
})
|
})
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
addDropboxAccount() {
|
addDropboxAccount() {
|
||||||
return this.$store.dispatch('modal/open', {
|
return this.$store.dispatch('modal/open', {
|
||||||
type: 'dropboxAccount',
|
type: 'dropboxAccount',
|
||||||
onResolve: () => dropboxHelper.addAccount(!store.getters['data/localSettings'].dropboxRestrictedAccess),
|
onResolve: () => dropboxHelper.addAccount(!store.getters['data/localSettings'].dropboxRestrictedAccess),
|
||||||
})
|
})
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
addGithubAccount() {
|
addGithubAccount() {
|
||||||
return this.$store.dispatch('modal/open', {
|
return this.$store.dispatch('modal/open', {
|
||||||
type: 'githubAccount',
|
type: 'githubAccount',
|
||||||
onResolve: () => githubHelper.addAccount(store.getters['data/localSettings'].githubRepoFullAccess),
|
onResolve: () => githubHelper.addAccount(store.getters['data/localSettings'].githubRepoFullAccess),
|
||||||
})
|
})
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
addWordpressAccount() {
|
addWordpressAccount() {
|
||||||
return wordpressHelper.addAccount()
|
return wordpressHelper.addAccount()
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
addBloggerAccount() {
|
addBloggerAccount() {
|
||||||
return googleHelper.addBloggerAccount()
|
return googleHelper.addBloggerAccount()
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
addZendeskAccount() {
|
addZendeskAccount() {
|
||||||
return this.$store.dispatch('modal/open', {
|
return this.$store.dispatch('modal/open', {
|
||||||
type: 'zendeskAccount',
|
type: 'zendeskAccount',
|
||||||
onResolve: ({ subdomain, clientId }) => zendeskHelper.addAccount(subdomain, clientId),
|
onResolve: ({ subdomain, clientId }) => zendeskHelper.addAccount(subdomain, clientId),
|
||||||
})
|
})
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
publishGoogleDrive(token) {
|
publishGoogleDrive(token) {
|
||||||
return openPublishModal(token, 'googleDrivePublish')
|
return openPublishModal(token, 'googleDrivePublish')
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
publishDropbox(token) {
|
publishDropbox(token) {
|
||||||
return openPublishModal(token, 'dropboxPublish')
|
return openPublishModal(token, 'dropboxPublish')
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
publishGithub(token) {
|
publishGithub(token) {
|
||||||
return openPublishModal(token, 'githubPublish')
|
return openPublishModal(token, 'githubPublish')
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
publishGist(token) {
|
publishGist(token) {
|
||||||
return openPublishModal(token, 'gistPublish')
|
return openPublishModal(token, 'gistPublish')
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
publishWordpress(token) {
|
publishWordpress(token) {
|
||||||
return openPublishModal(token, 'wordpressPublish')
|
return openPublishModal(token, 'wordpressPublish')
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
publishBlogger(token) {
|
publishBlogger(token) {
|
||||||
return openPublishModal(token, 'bloggerPublish')
|
return openPublishModal(token, 'bloggerPublish')
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
publishBloggerPage(token) {
|
publishBloggerPage(token) {
|
||||||
return openPublishModal(token, 'bloggerPagePublish')
|
return openPublishModal(token, 'bloggerPagePublish')
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
publishZendesk(token) {
|
publishZendesk(token) {
|
||||||
return openPublishModal(token, 'zendeskPublish')
|
return openPublishModal(token, 'zendeskPublish')
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -101,6 +101,8 @@ const openSyncModal = (token, type) => store.dispatch('modal/open', {
|
|||||||
token,
|
token,
|
||||||
}).then(syncLocation => syncSvc.createSyncLocation(syncLocation));
|
}).then(syncLocation => syncSvc.createSyncLocation(syncLocation));
|
||||||
|
|
||||||
|
const onCancel = () => {};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
MenuEntry,
|
MenuEntry,
|
||||||
@ -150,21 +152,21 @@ export default {
|
|||||||
type: 'googleDriveAccount',
|
type: 'googleDriveAccount',
|
||||||
onResolve: () => googleHelper.addDriveAccount(!store.getters['data/localSettings'].googleDriveRestrictedAccess),
|
onResolve: () => googleHelper.addDriveAccount(!store.getters['data/localSettings'].googleDriveRestrictedAccess),
|
||||||
})
|
})
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
addDropboxAccount() {
|
addDropboxAccount() {
|
||||||
return this.$store.dispatch('modal/open', {
|
return this.$store.dispatch('modal/open', {
|
||||||
type: 'dropboxAccount',
|
type: 'dropboxAccount',
|
||||||
onResolve: () => dropboxHelper.addAccount(!store.getters['data/localSettings'].dropboxRestrictedAccess),
|
onResolve: () => dropboxHelper.addAccount(!store.getters['data/localSettings'].dropboxRestrictedAccess),
|
||||||
})
|
})
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
addGithubAccount() {
|
addGithubAccount() {
|
||||||
return this.$store.dispatch('modal/open', {
|
return this.$store.dispatch('modal/open', {
|
||||||
type: 'githubAccount',
|
type: 'githubAccount',
|
||||||
onResolve: () => githubHelper.addAccount(store.getters['data/localSettings'].githubRepoFullAccess),
|
onResolve: () => githubHelper.addAccount(store.getters['data/localSettings'].githubRepoFullAccess),
|
||||||
})
|
})
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
openGoogleDrive(token) {
|
openGoogleDrive(token) {
|
||||||
return googleHelper.openPicker(token, 'doc')
|
return googleHelper.openPicker(token, 'doc')
|
||||||
@ -178,11 +180,11 @@ export default {
|
|||||||
},
|
},
|
||||||
saveGoogleDrive(token) {
|
saveGoogleDrive(token) {
|
||||||
return openSyncModal(token, 'googleDriveSave')
|
return openSyncModal(token, 'googleDriveSave')
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
saveDropbox(token) {
|
saveDropbox(token) {
|
||||||
return openSyncModal(token, 'dropboxSave')
|
return openSyncModal(token, 'dropboxSave')
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
openGithub(token) {
|
openGithub(token) {
|
||||||
return store.dispatch('modal/open', {
|
return store.dispatch('modal/open', {
|
||||||
@ -194,11 +196,11 @@ export default {
|
|||||||
},
|
},
|
||||||
saveGithub(token) {
|
saveGithub(token) {
|
||||||
return openSyncModal(token, 'githubSave')
|
return openSyncModal(token, 'githubSave')
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
saveGist(token) {
|
saveGist(token) {
|
||||||
return openSyncModal(token, 'gistSync')
|
return openSyncModal(token, 'gistSync')
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -31,6 +31,8 @@ import { mapGetters } from 'vuex';
|
|||||||
import MenuEntry from './common/MenuEntry';
|
import MenuEntry from './common/MenuEntry';
|
||||||
import googleHelper from '../../services/providers/helpers/googleHelper';
|
import googleHelper from '../../services/providers/helpers/googleHelper';
|
||||||
|
|
||||||
|
const onCancel = () => {};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
MenuEntry,
|
MenuEntry,
|
||||||
@ -48,13 +50,13 @@ export default {
|
|||||||
return this.$store.dispatch('modal/open', {
|
return this.$store.dispatch('modal/open', {
|
||||||
type: 'couchdbWorkspace',
|
type: 'couchdbWorkspace',
|
||||||
})
|
})
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
addGithubWorkspace() {
|
addGithubWorkspace() {
|
||||||
return this.$store.dispatch('modal/open', {
|
return this.$store.dispatch('modal/open', {
|
||||||
type: 'githubWorkspace',
|
type: 'githubWorkspace',
|
||||||
})
|
})
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
addGoogleDriveWorkspace() {
|
addGoogleDriveWorkspace() {
|
||||||
return googleHelper.addDriveAccount(true)
|
return googleHelper.addDriveAccount(true)
|
||||||
@ -62,7 +64,7 @@ export default {
|
|||||||
type: 'googleDriveWorkspace',
|
type: 'googleDriveWorkspace',
|
||||||
token,
|
token,
|
||||||
}))
|
}))
|
||||||
.catch(() => {}); // Cancel
|
.catch(onCancel);
|
||||||
},
|
},
|
||||||
manageWorkspaces() {
|
manageWorkspaces() {
|
||||||
return this.$store.dispatch('modal/open', 'workspaceManagement');
|
return this.$store.dispatch('modal/open', 'workspaceManagement');
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<modal-inner class="modal__inner-1--about-modal" aria-label="About">
|
<modal-inner class="modal__inner-1--about-modal" aria-label="About">
|
||||||
<div class="modal__content">
|
<div class="modal__content">
|
||||||
<div class="logo-background"></div>
|
<div class="logo-background"></div>
|
||||||
<small>v{{version}} — © 2018 Dock5 Software</small>
|
<small>v{{version}}<br>© 2013-2018 Dock5 Software</small>
|
||||||
<hr>
|
<hr>
|
||||||
StackEdit on <a target="_blank" href="https://github.com/benweet/stackedit/">GitHub</a>
|
StackEdit on <a target="_blank" href="https://github.com/benweet/stackedit/">GitHub</a>
|
||||||
<br>
|
<br>
|
||||||
|
@ -70,7 +70,8 @@ export default modalTemplate({
|
|||||||
if (err.status !== 401) {
|
if (err.status !== 401) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
this.$store.dispatch('modal/sponsorOnly');
|
this.$store.dispatch('modal/sponsorOnly')
|
||||||
|
.catch(() => { /* Cancel */ });
|
||||||
}))
|
}))
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error(err); // eslint-disable-line no-console
|
console.error(err); // eslint-disable-line no-console
|
||||||
|
@ -63,7 +63,8 @@ export default modalTemplate({
|
|||||||
if (err.status !== 401) {
|
if (err.status !== 401) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
this.$store.dispatch('modal/sponsorOnly');
|
this.$store.dispatch('modal/sponsorOnly')
|
||||||
|
.catch(() => { /* Cancel */ });
|
||||||
}))
|
}))
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error(err); // eslint-disable-line no-console
|
console.error(err); // eslint-disable-line no-console
|
||||||
|
@ -79,7 +79,7 @@ export default {
|
|||||||
return this.$store.dispatch('modal/removeWorkspace')
|
return this.$store.dispatch('modal/removeWorkspace')
|
||||||
.then(
|
.then(
|
||||||
() => localDbSvc.removeWorkspace(id),
|
() => localDbSvc.removeWorkspace(id),
|
||||||
() => {}, // Cancel
|
() => { /* Cancel */ },
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -42,7 +42,7 @@ export default {
|
|||||||
this.$store.dispatch('modal/open', 'sponsor');
|
this.$store.dispatch('modal/open', 'sponsor');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {}); // Cancel
|
.catch(() => { /* Cancel */ });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import store from '../store';
|
import fileSvc from './fileSvc';
|
||||||
import utils from './utils';
|
import utils from './utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
importBackup(jsonValue) {
|
async importBackup(jsonValue) {
|
||||||
const nameMap = {};
|
const fileNameMap = {};
|
||||||
|
const folderNameMap = {};
|
||||||
const parentIdMap = {};
|
const parentIdMap = {};
|
||||||
const textMap = {};
|
const textMap = {};
|
||||||
const propertiesMap = {};
|
const propertiesMap = {};
|
||||||
@ -22,24 +23,18 @@ export default {
|
|||||||
// StackEdit v4 format
|
// StackEdit v4 format
|
||||||
const [, v4Id, type] = v4Match;
|
const [, v4Id, type] = v4Match;
|
||||||
if (type === 'title') {
|
if (type === 'title') {
|
||||||
nameMap[v4Id] = value;
|
fileNameMap[v4Id] = value;
|
||||||
} else if (type === 'content') {
|
} else if (type === 'content') {
|
||||||
textMap[v4Id] = value;
|
textMap[v4Id] = value;
|
||||||
}
|
}
|
||||||
} else if (value.type === 'folder') {
|
} else if (value.type === 'folder') {
|
||||||
// StackEdit v5 folder
|
// StackEdit v5 folder
|
||||||
const folderId = utils.uid();
|
folderIdMap[id] = utils.uid();
|
||||||
const name = utils.sanitizeName(value.name);
|
folderNameMap[id] = value.name;
|
||||||
const parentId = `${value.parentId || ''}` || null;
|
parentIdMap[id] = `${value.parentId || ''}`;
|
||||||
store.commit('folder/setItem', {
|
|
||||||
id: folderId,
|
|
||||||
name,
|
|
||||||
parentId,
|
|
||||||
});
|
|
||||||
folderIdMap[id] = folderId;
|
|
||||||
} else if (value.type === 'file') {
|
} else if (value.type === 'file') {
|
||||||
// StackEdit v5 file
|
// StackEdit v5 file
|
||||||
nameMap[id] = utils.sanitizeName(value.name);
|
fileNameMap[id] = value.name;
|
||||||
parentIdMap[id] = `${value.parentId || ''}`;
|
parentIdMap[id] = `${value.parentId || ''}`;
|
||||||
} else if (value.type === 'content') {
|
} else if (value.type === 'content') {
|
||||||
// StackEdit v5 content
|
// StackEdit v5 content
|
||||||
@ -54,14 +49,20 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Go through the maps
|
await utils.awaitSequence(Object.keys(folderNameMap), async externalId => fileSvc.storeItem({
|
||||||
Object.entries(nameMap).forEach(([externalId, name]) => store.dispatch('createFile', {
|
id: folderIdMap[externalId],
|
||||||
name,
|
type: 'folder',
|
||||||
|
name: folderNameMap[externalId],
|
||||||
|
parentId: folderIdMap[parentIdMap[externalId]],
|
||||||
|
}, true));
|
||||||
|
|
||||||
|
await utils.awaitSequence(Object.keys(fileNameMap), async externalId => fileSvc.createFile({
|
||||||
|
name: fileNameMap[externalId],
|
||||||
parentId: folderIdMap[parentIdMap[externalId]],
|
parentId: folderIdMap[parentIdMap[externalId]],
|
||||||
text: textMap[externalId],
|
text: textMap[externalId],
|
||||||
properties: propertiesMap[externalId],
|
properties: propertiesMap[externalId],
|
||||||
discussions: discussionsMap[externalId],
|
discussions: discussionsMap[externalId],
|
||||||
comments: commentsMap[externalId],
|
comments: commentsMap[externalId],
|
||||||
}));
|
}, true));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
85
src/services/explorerSvc.js
Normal file
85
src/services/explorerSvc.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import store from '../store';
|
||||||
|
import fileSvc from './fileSvc';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
newItem(isFolder = false) {
|
||||||
|
let parentId = store.getters['explorer/selectedNodeFolder'].item.id;
|
||||||
|
if (parentId === 'trash' // Not allowed to create new items in the trash
|
||||||
|
|| (isFolder && parentId === 'temp') // Not allowed to create new folders in the temp folder
|
||||||
|
) {
|
||||||
|
parentId = null;
|
||||||
|
}
|
||||||
|
store.dispatch('explorer/openNode', parentId);
|
||||||
|
store.commit('explorer/setNewItem', {
|
||||||
|
type: isFolder ? 'folder' : 'file',
|
||||||
|
parentId,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deleteItem() {
|
||||||
|
const selectedNode = store.getters['explorer/selectedNode'];
|
||||||
|
if (selectedNode.isNil) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
if (selectedNode.isTrash || selectedNode.item.parentId === 'trash') {
|
||||||
|
return store.dispatch('modal/trashDeletion').catch(() => { /* Cancel */ });
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if we have a dialog to show
|
||||||
|
let modalAction;
|
||||||
|
let moveToTrash = true;
|
||||||
|
if (selectedNode.isTemp) {
|
||||||
|
modalAction = 'modal/tempFolderDeletion';
|
||||||
|
moveToTrash = false;
|
||||||
|
} else if (selectedNode.item.parentId === 'temp') {
|
||||||
|
modalAction = 'modal/tempFileDeletion';
|
||||||
|
moveToTrash = false;
|
||||||
|
} else if (selectedNode.isFolder) {
|
||||||
|
modalAction = 'modal/folderDeletion';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (modalAction
|
||||||
|
? store.dispatch(modalAction, selectedNode.item)
|
||||||
|
: Promise.resolve())
|
||||||
|
.then(() => {
|
||||||
|
const deleteFile = (id) => {
|
||||||
|
if (moveToTrash) {
|
||||||
|
store.commit('file/patchItem', {
|
||||||
|
id,
|
||||||
|
parentId: 'trash',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fileSvc.deleteFile(id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (selectedNode === store.getters['explorer/selectedNode']) {
|
||||||
|
const currentFileId = store.getters['file/current'].id;
|
||||||
|
let doClose = selectedNode.item.id === currentFileId;
|
||||||
|
if (selectedNode.isFolder) {
|
||||||
|
const recursiveDelete = (folderNode) => {
|
||||||
|
folderNode.folders.forEach(recursiveDelete);
|
||||||
|
folderNode.files.forEach((fileNode) => {
|
||||||
|
doClose = doClose || fileNode.item.id === currentFileId;
|
||||||
|
deleteFile(fileNode.item.id);
|
||||||
|
});
|
||||||
|
store.commit('folder/deleteItem', folderNode.item.id);
|
||||||
|
};
|
||||||
|
recursiveDelete(selectedNode);
|
||||||
|
} else {
|
||||||
|
deleteFile(selectedNode.item.id);
|
||||||
|
}
|
||||||
|
if (doClose) {
|
||||||
|
// Close the current file by opening the last opened, not deleted one
|
||||||
|
store.getters['data/lastOpenedIds'].some((id) => {
|
||||||
|
const file = store.state.file.itemMap[id];
|
||||||
|
if (file.parentId === 'trash') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
store.commit('file/setCurrentId', id);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, () => { /* Cancel */ });
|
||||||
|
},
|
||||||
|
};
|
162
src/services/fileSvc.js
Normal file
162
src/services/fileSvc.js
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
import store from '../store';
|
||||||
|
import utils from './utils';
|
||||||
|
|
||||||
|
const forbiddenFolderNameMatcher = /^\.stackedit-data$|^\.stackedit-trash$|\.md$|\.sync$|\.publish$/;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* Create a file in the store with the specified fields.
|
||||||
|
*/
|
||||||
|
createFile(fields = {}, background = false) {
|
||||||
|
const id = utils.uid();
|
||||||
|
const file = {
|
||||||
|
id,
|
||||||
|
name: utils.sanitizeName(fields.name),
|
||||||
|
parentId: fields.parentId || null,
|
||||||
|
};
|
||||||
|
const content = {
|
||||||
|
id: `${id}/content`,
|
||||||
|
text: utils.sanitizeText(fields.text || store.getters['data/computedSettings'].newFileContent),
|
||||||
|
properties: utils.sanitizeText(
|
||||||
|
fields.properties || store.getters['data/computedSettings'].newFileProperties),
|
||||||
|
discussions: fields.discussions || {},
|
||||||
|
comments: fields.comments || {},
|
||||||
|
};
|
||||||
|
const nameStripped = file.name !== utils.defaultName && file.name !== fields.name;
|
||||||
|
|
||||||
|
// Check if there is a path conflict
|
||||||
|
const workspaceUniquePaths = store.getters['workspace/hasUniquePaths'];
|
||||||
|
let pathConflict;
|
||||||
|
if (workspaceUniquePaths) {
|
||||||
|
const parentPath = store.getters.itemPaths[file.parentId] || '';
|
||||||
|
const path = parentPath + file.name;
|
||||||
|
pathConflict = !!store.getters.pathItems[path];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show warning dialogs and then save in the store
|
||||||
|
return Promise.resolve()
|
||||||
|
.then(() => !background && nameStripped && store.dispatch('modal/stripName', fields.name))
|
||||||
|
.then(() => !background && pathConflict && store.dispatch('modal/pathConflict', fields.name))
|
||||||
|
.then(() => {
|
||||||
|
store.commit('content/setItem', content);
|
||||||
|
store.commit('file/setItem', file);
|
||||||
|
if (workspaceUniquePaths) {
|
||||||
|
this.makePathUnique(id);
|
||||||
|
}
|
||||||
|
return store.state.file.itemMap[id];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sanity checks and then create/update the folder/file in the store.
|
||||||
|
*/
|
||||||
|
async storeItem(item, background = false) {
|
||||||
|
const id = item.id || utils.uid();
|
||||||
|
const sanitizedName = utils.sanitizeName(item.name);
|
||||||
|
|
||||||
|
if (item.type === 'folder' && forbiddenFolderNameMatcher.exec(sanitizedName)) {
|
||||||
|
if (background) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
await store.dispatch('modal/unauthorizedName', item.name);
|
||||||
|
throw new Error('Unauthorized name.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const workspaceUniquePaths = store.getters['workspace/hasUniquePaths'];
|
||||||
|
|
||||||
|
// Show warning dialogs
|
||||||
|
if (!background) {
|
||||||
|
// If name has been stripped
|
||||||
|
if (sanitizedName !== utils.defaultName && sanitizedName !== item.name) {
|
||||||
|
await store.dispatch('modal/stripName', item.name);
|
||||||
|
}
|
||||||
|
// Check if there is a path conflict
|
||||||
|
if (workspaceUniquePaths) {
|
||||||
|
const parentPath = store.getters.itemPaths[item.parentId] || '';
|
||||||
|
const path = parentPath + sanitizedName;
|
||||||
|
const pathItems = store.getters.pathItems[path] || [];
|
||||||
|
if (pathItems.some(itemWithSamePath => itemWithSamePath.id !== id)) {
|
||||||
|
await store.dispatch('modal/pathConflict', item.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save item in the store
|
||||||
|
store.commit(`${item.type}/setItem`, {
|
||||||
|
id,
|
||||||
|
parentId: item.parentId || null,
|
||||||
|
name: sanitizedName,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ensure path uniqueness
|
||||||
|
if (workspaceUniquePaths) {
|
||||||
|
this.makePathUnique(id);
|
||||||
|
}
|
||||||
|
return store.getters.allItemMap[id];
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a file in the store and all its related items.
|
||||||
|
*/
|
||||||
|
deleteFile(fileId) {
|
||||||
|
// Delete the file
|
||||||
|
store.commit('file/deleteItem', fileId);
|
||||||
|
// Delete the content
|
||||||
|
store.commit('content/deleteItem', `${fileId}/content`);
|
||||||
|
// Delete the syncedContent
|
||||||
|
store.commit('syncedContent/deleteItem', `${fileId}/syncedContent`);
|
||||||
|
// Delete the contentState
|
||||||
|
store.commit('contentState/deleteItem', `${fileId}/contentState`);
|
||||||
|
// Delete sync locations
|
||||||
|
(store.getters['syncLocation/groupedByFileId'][fileId] || [])
|
||||||
|
.forEach(item => store.commit('syncLocation/deleteItem', item.id));
|
||||||
|
// Delete publish locations
|
||||||
|
(store.getters['publishLocation/groupedByFileId'][fileId] || [])
|
||||||
|
.forEach(item => store.commit('publishLocation/deleteItem', item.id));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure two files/folders don't have the same path if the workspace doesn't support it.
|
||||||
|
*/
|
||||||
|
ensureUniquePaths() {
|
||||||
|
if (store.getters['workspace/hasUniquePaths']) {
|
||||||
|
if (Object.keys(store.getters.itemPaths).some(id => this.makePathUnique(id))) {
|
||||||
|
this.ensureUniquePaths();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return false if the file/folder path is unique.
|
||||||
|
* Add a prefix to its name and return true otherwise.
|
||||||
|
*/
|
||||||
|
makePathUnique(id) {
|
||||||
|
const item = store.getters.allItemMap[id];
|
||||||
|
if (!item) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let path = store.getters.itemPaths[id];
|
||||||
|
const pathItems = store.getters.pathItems;
|
||||||
|
if (pathItems[path].length === 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const isFolder = item.type === 'folder';
|
||||||
|
if (isFolder) {
|
||||||
|
// Remove trailing slash
|
||||||
|
path = path.slice(0, -1);
|
||||||
|
}
|
||||||
|
for (let suffix = 1; ; suffix += 1) {
|
||||||
|
let pathWithPrefix = `${path}.${suffix}`;
|
||||||
|
if (isFolder) {
|
||||||
|
pathWithPrefix += '/';
|
||||||
|
}
|
||||||
|
if (!pathItems[pathWithPrefix]) {
|
||||||
|
store.commit(`${item.type}/patchItem`, {
|
||||||
|
id: item.id,
|
||||||
|
name: `${item.name}.${suffix}`,
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
@ -2,6 +2,7 @@ import FileSaver from 'file-saver';
|
|||||||
import utils from './utils';
|
import utils from './utils';
|
||||||
import store from '../store';
|
import store from '../store';
|
||||||
import welcomeFile from '../data/welcomeFile.md';
|
import welcomeFile from '../data/welcomeFile.md';
|
||||||
|
import fileSvc from './fileSvc';
|
||||||
|
|
||||||
const dbVersion = 1;
|
const dbVersion = 1;
|
||||||
const dbStoreName = 'objects';
|
const dbStoreName = 'objects';
|
||||||
@ -186,6 +187,7 @@ const localDbSvc = {
|
|||||||
dbStore.delete(item.id);
|
dbStore.delete(item.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
fileSvc.ensureUniquePaths();
|
||||||
this.lastTx = lastTx;
|
this.lastTx = lastTx;
|
||||||
cb(storeItemMap);
|
cb(storeItemMap);
|
||||||
}
|
}
|
||||||
@ -249,20 +251,20 @@ const localDbSvc = {
|
|||||||
* Read and apply one DB change.
|
* Read and apply one DB change.
|
||||||
*/
|
*/
|
||||||
readDbItem(dbItem, storeItemMap) {
|
readDbItem(dbItem, storeItemMap) {
|
||||||
const existingStoreItem = storeItemMap[dbItem.id];
|
const storeItem = storeItemMap[dbItem.id];
|
||||||
if (!dbItem.hash) {
|
if (!dbItem.hash) {
|
||||||
// DB item is a delete marker
|
// DB item is a delete marker
|
||||||
delete this.hashMap[dbItem.type][dbItem.id];
|
delete this.hashMap[dbItem.type][dbItem.id];
|
||||||
if (existingStoreItem) {
|
if (storeItem) {
|
||||||
// Remove item from the store
|
// Remove item from the store
|
||||||
store.commit(`${existingStoreItem.type}/deleteItem`, existingStoreItem.id);
|
store.commit(`${storeItem.type}/deleteItem`, storeItem.id);
|
||||||
delete storeItemMap[existingStoreItem.id];
|
delete storeItemMap[storeItem.id];
|
||||||
}
|
}
|
||||||
} else if (this.hashMap[dbItem.type][dbItem.id] !== dbItem.hash) {
|
} else if (this.hashMap[dbItem.type][dbItem.id] !== dbItem.hash) {
|
||||||
// DB item is different from the corresponding store item
|
// DB item is different from the corresponding store item
|
||||||
this.hashMap[dbItem.type][dbItem.id] = dbItem.hash;
|
this.hashMap[dbItem.type][dbItem.id] = dbItem.hash;
|
||||||
// Update content only if it exists in the store
|
// Update content only if it exists in the store
|
||||||
if (existingStoreItem || !contentTypes[dbItem.type] || exportWorkspace) {
|
if (storeItem || !contentTypes[dbItem.type] || exportWorkspace) {
|
||||||
// Put item in the store
|
// Put item in the store
|
||||||
dbItem.tx = undefined;
|
dbItem.tx = undefined;
|
||||||
store.commit(`${dbItem.type}/setItem`, dbItem);
|
store.commit(`${dbItem.type}/setItem`, dbItem);
|
||||||
@ -403,13 +405,14 @@ const localDbSvc = {
|
|||||||
// Clean files
|
// Clean files
|
||||||
store.getters['file/items']
|
store.getters['file/items']
|
||||||
.filter(file => file.parentId === 'trash') // If file is in the trash
|
.filter(file => file.parentId === 'trash') // If file is in the trash
|
||||||
.forEach(file => store.dispatch('deleteFile', file.id));
|
.forEach(file => fileSvc.deleteFile(file.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable sponsorship
|
// Enable sponsorship
|
||||||
if (utils.queryParams.paymentSuccess) {
|
if (utils.queryParams.paymentSuccess) {
|
||||||
location.hash = ''; // PaymentSuccess param is always on its own
|
location.hash = ''; // PaymentSuccess param is always on its own
|
||||||
store.dispatch('modal/paymentSuccess');
|
store.dispatch('modal/paymentSuccess')
|
||||||
|
.catch(() => { /* Cancel */ });
|
||||||
const sponsorToken = store.getters['workspace/sponsorToken'];
|
const sponsorToken = store.getters['workspace/sponsorToken'];
|
||||||
// Force check sponsorship after a few seconds
|
// Force check sponsorship after a few seconds
|
||||||
const currentDate = Date.now();
|
const currentDate = Date.now();
|
||||||
@ -438,10 +441,10 @@ const localDbSvc = {
|
|||||||
store.commit('file/setCurrentId', recentFile.id);
|
store.commit('file/setCurrentId', recentFile.id);
|
||||||
} else {
|
} else {
|
||||||
// If still no ID, create a new file
|
// If still no ID, create a new file
|
||||||
store.dispatch('createFile', {
|
fileSvc.createFile({
|
||||||
name: 'Welcome file',
|
name: 'Welcome file',
|
||||||
text: welcomeFile,
|
text: welcomeFile,
|
||||||
})
|
}, true)
|
||||||
// Set it as the current file
|
// Set it as the current file
|
||||||
.then(newFile => store.commit('file/setCurrentId', newFile.id));
|
.then(newFile => store.commit('file/setCurrentId', newFile.id));
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,9 @@ export default class Provider {
|
|||||||
parentId: null,
|
parentId: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import store from '../../store';
|
|||||||
import dropboxHelper from './helpers/dropboxHelper';
|
import dropboxHelper from './helpers/dropboxHelper';
|
||||||
import Provider from './common/Provider';
|
import Provider from './common/Provider';
|
||||||
import utils from '../utils';
|
import utils from '../utils';
|
||||||
|
import fileSvc from '../fileSvc';
|
||||||
|
|
||||||
const makePathAbsolute = (token, path) => {
|
const makePathAbsolute = (token, path) => {
|
||||||
if (!token.fullAccess) {
|
if (!token.fullAccess) {
|
||||||
@ -88,12 +89,6 @@ export default new Provider({
|
|||||||
};
|
};
|
||||||
return this.downloadContent(token, syncLocation)
|
return this.downloadContent(token, syncLocation)
|
||||||
.then((content) => {
|
.then((content) => {
|
||||||
const id = utils.uid();
|
|
||||||
delete content.history;
|
|
||||||
store.commit('content/setItem', {
|
|
||||||
...content,
|
|
||||||
id: `${id}/content`,
|
|
||||||
});
|
|
||||||
let name = path;
|
let name = path;
|
||||||
const slashPos = name.lastIndexOf('/');
|
const slashPos = name.lastIndexOf('/');
|
||||||
if (slashPos > -1 && slashPos < name.length - 1) {
|
if (slashPos > -1 && slashPos < name.length - 1) {
|
||||||
@ -103,25 +98,30 @@ export default new Provider({
|
|||||||
if (dotPos > 0 && slashPos < name.length) {
|
if (dotPos > 0 && slashPos < name.length) {
|
||||||
name = name.slice(0, dotPos);
|
name = name.slice(0, dotPos);
|
||||||
}
|
}
|
||||||
store.commit('file/setItem', {
|
return fileSvc.createFile({
|
||||||
id,
|
name,
|
||||||
name: utils.sanitizeName(name),
|
|
||||||
parentId: store.getters['file/current'].parentId,
|
parentId: store.getters['file/current'].parentId,
|
||||||
});
|
text: content.text,
|
||||||
|
properties: content.properties,
|
||||||
|
discussions: content.discussions,
|
||||||
|
comments: content.comments,
|
||||||
|
}, true);
|
||||||
|
})
|
||||||
|
.then((item) => {
|
||||||
|
store.commit('file/setCurrentId', item.id);
|
||||||
store.commit('syncLocation/setItem', {
|
store.commit('syncLocation/setItem', {
|
||||||
...syncLocation,
|
...syncLocation,
|
||||||
id: utils.uid(),
|
id: utils.uid(),
|
||||||
fileId: id,
|
fileId: item.id,
|
||||||
});
|
});
|
||||||
store.commit('file/setCurrentId', id);
|
|
||||||
store.dispatch('notification/info', `${store.getters['file/current'].name} was imported from Dropbox.`);
|
store.dispatch('notification/info', `${store.getters['file/current'].name} was imported from Dropbox.`);
|
||||||
}, () => {
|
})
|
||||||
|
.catch(() => {
|
||||||
store.dispatch('notification/error', `Could not open file ${path}.`);
|
store.dispatch('notification/error', `Could not open file ${path}.`);
|
||||||
})
|
})
|
||||||
.then(() => openOneFile());
|
.then(() => openOneFile());
|
||||||
};
|
};
|
||||||
return Promise.resolve()
|
return Promise.resolve(openOneFile());
|
||||||
.then(() => openOneFile());
|
|
||||||
},
|
},
|
||||||
makeLocation(token, path) {
|
makeLocation(token, path) {
|
||||||
return {
|
return {
|
||||||
|
@ -2,6 +2,7 @@ import store from '../../store';
|
|||||||
import githubHelper from './helpers/githubHelper';
|
import githubHelper from './helpers/githubHelper';
|
||||||
import Provider from './common/Provider';
|
import Provider from './common/Provider';
|
||||||
import utils from '../utils';
|
import utils from '../utils';
|
||||||
|
import fileSvc from '../fileSvc';
|
||||||
|
|
||||||
const savedSha = {};
|
const savedSha = {};
|
||||||
|
|
||||||
@ -75,12 +76,6 @@ export default new Provider({
|
|||||||
// Download content from GitHub and create the file
|
// Download content from GitHub and create the file
|
||||||
return this.downloadContent(token, syncLocation)
|
return this.downloadContent(token, syncLocation)
|
||||||
.then((content) => {
|
.then((content) => {
|
||||||
const id = utils.uid();
|
|
||||||
delete content.history;
|
|
||||||
store.commit('content/setItem', {
|
|
||||||
...content,
|
|
||||||
id: `${id}/content`,
|
|
||||||
});
|
|
||||||
let name = syncLocation.path;
|
let name = syncLocation.path;
|
||||||
const slashPos = name.lastIndexOf('/');
|
const slashPos = name.lastIndexOf('/');
|
||||||
if (slashPos > -1 && slashPos < name.length - 1) {
|
if (slashPos > -1 && slashPos < name.length - 1) {
|
||||||
@ -90,19 +85,25 @@ export default new Provider({
|
|||||||
if (dotPos > 0 && slashPos < name.length) {
|
if (dotPos > 0 && slashPos < name.length) {
|
||||||
name = name.slice(0, dotPos);
|
name = name.slice(0, dotPos);
|
||||||
}
|
}
|
||||||
store.commit('file/setItem', {
|
return fileSvc.createFile({
|
||||||
id,
|
name,
|
||||||
name: utils.sanitizeName(name),
|
|
||||||
parentId: store.getters['file/current'].parentId,
|
parentId: store.getters['file/current'].parentId,
|
||||||
});
|
text: content.text,
|
||||||
|
properties: content.properties,
|
||||||
|
discussions: content.discussions,
|
||||||
|
comments: content.comments,
|
||||||
|
}, true);
|
||||||
|
})
|
||||||
|
.then((item) => {
|
||||||
|
store.commit('file/setCurrentId', item.id);
|
||||||
store.commit('syncLocation/setItem', {
|
store.commit('syncLocation/setItem', {
|
||||||
...syncLocation,
|
...syncLocation,
|
||||||
id: utils.uid(),
|
id: utils.uid(),
|
||||||
fileId: id,
|
fileId: item.id,
|
||||||
});
|
});
|
||||||
store.commit('file/setCurrentId', id);
|
|
||||||
store.dispatch('notification/info', `${store.getters['file/current'].name} was imported from GitHub.`);
|
store.dispatch('notification/info', `${store.getters['file/current'].name} was imported from GitHub.`);
|
||||||
}, () => {
|
})
|
||||||
|
.catch(() => {
|
||||||
store.dispatch('notification/error', `Could not open file ${syncLocation.path}.`);
|
store.dispatch('notification/error', `Could not open file ${syncLocation.path}.`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -479,12 +479,13 @@ export default new Provider({
|
|||||||
} else if (entry.committer && entry.committer.login) {
|
} else if (entry.committer && entry.committer.login) {
|
||||||
user = entry.committer;
|
user = entry.committer;
|
||||||
}
|
}
|
||||||
userSvc.addInfo({ id: user.login, name: user.login, imageUrl: user.avatar_url });
|
const sub = `gh:${user.id}`;
|
||||||
|
userSvc.addInfo({ id: sub, name: user.login, imageUrl: user.avatar_url });
|
||||||
const date = (entry.commit.author && entry.commit.author.date)
|
const date = (entry.commit.author && entry.commit.author.date)
|
||||||
|| (entry.commit.committer && entry.commit.committer.date);
|
|| (entry.commit.committer && entry.commit.committer.date);
|
||||||
return {
|
return {
|
||||||
id: entry.sha,
|
id: entry.sha,
|
||||||
sub: user.login,
|
sub,
|
||||||
created: date ? new Date(date).getTime() : 1,
|
created: date ? new Date(date).getTime() : 1,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
@ -136,7 +136,7 @@ export default new Provider({
|
|||||||
return googleHelper.getAppDataFileRevisions(token, syncData.id)
|
return googleHelper.getAppDataFileRevisions(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 && `go:${revision.lastModifyingUser.permissionId}`,
|
||||||
created: new Date(revision.modifiedTime).getTime(),
|
created: new Date(revision.modifiedTime).getTime(),
|
||||||
}))
|
}))
|
||||||
.sort((revision1, revision2) => revision2.created - revision1.created));
|
.sort((revision1, revision2) => revision2.created - revision1.created));
|
||||||
|
@ -2,6 +2,7 @@ import store from '../../store';
|
|||||||
import googleHelper from './helpers/googleHelper';
|
import googleHelper from './helpers/googleHelper';
|
||||||
import Provider from './common/Provider';
|
import Provider from './common/Provider';
|
||||||
import utils from '../utils';
|
import utils from '../utils';
|
||||||
|
import fileSvc from '../fileSvc';
|
||||||
|
|
||||||
export default new Provider({
|
export default new Provider({
|
||||||
id: 'googleDrive',
|
id: 'googleDrive',
|
||||||
@ -93,7 +94,7 @@ export default new Provider({
|
|||||||
const token = store.getters['data/googleTokens'][state.userId];
|
const token = store.getters['data/googleTokens'][state.userId];
|
||||||
switch (token && state.action) {
|
switch (token && state.action) {
|
||||||
case 'create':
|
case 'create':
|
||||||
return store.dispatch('createFile')
|
return fileSvc.createFile({}, true)
|
||||||
.then((file) => {
|
.then((file) => {
|
||||||
store.commit('file/setCurrentId', file.id);
|
store.commit('file/setCurrentId', file.id);
|
||||||
// Return a new syncLocation
|
// Return a new syncLocation
|
||||||
@ -169,32 +170,29 @@ export default new Provider({
|
|||||||
sub: token.sub,
|
sub: token.sub,
|
||||||
};
|
};
|
||||||
return this.downloadContent(token, syncLocation)
|
return this.downloadContent(token, syncLocation)
|
||||||
.then((content) => {
|
.then(content => fileSvc.createFile({
|
||||||
const id = utils.uid();
|
name,
|
||||||
delete content.history;
|
|
||||||
store.commit('content/setItem', {
|
|
||||||
...content,
|
|
||||||
id: `${id}/content`,
|
|
||||||
});
|
|
||||||
store.commit('file/setItem', {
|
|
||||||
id,
|
|
||||||
name: utils.sanitizeName(driveFile.name),
|
|
||||||
parentId: store.getters['file/current'].parentId,
|
parentId: store.getters['file/current'].parentId,
|
||||||
});
|
text: content.text,
|
||||||
|
properties: content.properties,
|
||||||
|
discussions: content.discussions,
|
||||||
|
comments: content.comments,
|
||||||
|
}, true))
|
||||||
|
.then((item) => {
|
||||||
|
store.commit('file/setCurrentId', item.id);
|
||||||
store.commit('syncLocation/setItem', {
|
store.commit('syncLocation/setItem', {
|
||||||
...syncLocation,
|
...syncLocation,
|
||||||
id: utils.uid(),
|
id: utils.uid(),
|
||||||
fileId: id,
|
fileId: item.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.`);
|
||||||
}, () => {
|
})
|
||||||
|
.catch(() => {
|
||||||
store.dispatch('notification/error', `Could not open file ${driveFile.id}.`);
|
store.dispatch('notification/error', `Could not open file ${driveFile.id}.`);
|
||||||
})
|
})
|
||||||
.then(() => openOneFile());
|
.then(() => openOneFile());
|
||||||
};
|
};
|
||||||
return Promise.resolve()
|
return Promise.resolve(openOneFile());
|
||||||
.then(() => openOneFile());
|
|
||||||
},
|
},
|
||||||
makeLocation(token, fileId, folderId) {
|
makeLocation(token, fileId, folderId) {
|
||||||
const location = {
|
const location = {
|
||||||
|
@ -2,6 +2,7 @@ import store from '../../store';
|
|||||||
import googleHelper from './helpers/googleHelper';
|
import googleHelper from './helpers/googleHelper';
|
||||||
import Provider from './common/Provider';
|
import Provider from './common/Provider';
|
||||||
import utils from '../utils';
|
import utils from '../utils';
|
||||||
|
import fileSvc from '../fileSvc';
|
||||||
|
|
||||||
const getSyncData = (fileId) => {
|
const getSyncData = (fileId) => {
|
||||||
const syncData = store.getters['data/syncDataByItemId'][`${fileId}/content`];
|
const syncData = store.getters['data/syncDataByItemId'][`${fileId}/content`];
|
||||||
@ -195,9 +196,9 @@ export default new Provider({
|
|||||||
[syncData.id]: syncData,
|
[syncData.id]: syncData,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return store.dispatch('createFile', {
|
return fileSvc.createFile({
|
||||||
parentId: syncData && syncData.itemId,
|
parentId: syncData && syncData.itemId,
|
||||||
})
|
}, true)
|
||||||
.then((file) => {
|
.then((file) => {
|
||||||
store.commit('file/setCurrentId', file.id);
|
store.commit('file/setCurrentId', file.id);
|
||||||
// File will be created on next workspace sync
|
// File will be created on next workspace sync
|
||||||
|
@ -91,9 +91,9 @@ export default {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: { item, time: Date.now() },
|
body: { item, time: Date.now() },
|
||||||
};
|
};
|
||||||
const loginToken = store.getters['workspace/loginToken'];
|
const userId = store.getters['workspace/userId'];
|
||||||
if (loginToken) {
|
if (userId) {
|
||||||
options.body.sub = loginToken.sub;
|
options.body.sub = userId;
|
||||||
}
|
}
|
||||||
if (documentId) {
|
if (documentId) {
|
||||||
options.method = 'PUT';
|
options.method = 'PUT';
|
||||||
|
@ -73,6 +73,22 @@ export default {
|
|||||||
addAccount(repoFullAccess = false) {
|
addAccount(repoFullAccess = false) {
|
||||||
return this.startOauth2(getScopes({ repoFullAccess }));
|
return this.startOauth2(getScopes({ repoFullAccess }));
|
||||||
},
|
},
|
||||||
|
getUser(userId) {
|
||||||
|
return networkSvc.request({
|
||||||
|
url: `https://api.github.com/user/${userId}`,
|
||||||
|
params: {
|
||||||
|
t: Date.now(), // Prevent from caching
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
store.commit('userInfo/addItem', {
|
||||||
|
id: `gh:${res.body.id}`,
|
||||||
|
name: res.body.login,
|
||||||
|
imageUrl: res.body.avatar_url || '',
|
||||||
|
});
|
||||||
|
return res.body;
|
||||||
|
});
|
||||||
|
},
|
||||||
getTree(token, owner, repo, sha) {
|
getTree(token, owner, repo, sha) {
|
||||||
return repoRequest(token, owner, repo, {
|
return repoRequest(token, owner, repo, {
|
||||||
url: `git/trees/${encodeURIComponent(sha)}?recursive=1`,
|
url: `git/trees/${encodeURIComponent(sha)}?recursive=1`,
|
||||||
@ -86,9 +102,9 @@ export default {
|
|||||||
},
|
},
|
||||||
getHeadTree(token, owner, repo, branch) {
|
getHeadTree(token, owner, repo, branch) {
|
||||||
return repoRequest(token, owner, repo, {
|
return repoRequest(token, owner, repo, {
|
||||||
url: `branches/${encodeURIComponent(branch)}`,
|
url: `commits/${encodeURIComponent(branch)}`,
|
||||||
})
|
})
|
||||||
.then(res => this.getTree(token, owner, repo, res.body.commit.commit.tree.sha));
|
.then(res => this.getTree(token, owner, repo, res.body.commit.tree.sha));
|
||||||
},
|
},
|
||||||
getCommits(token, owner, repo, sha, path) {
|
getCommits(token, owner, repo, sha, path) {
|
||||||
return repoRequest(token, owner, repo, {
|
return repoRequest(token, owner, repo, {
|
||||||
|
@ -203,7 +203,7 @@ export default {
|
|||||||
}, true)
|
}, true)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
store.commit('userInfo/addItem', {
|
store.commit('userInfo/addItem', {
|
||||||
id: res.body.id,
|
id: `go:${res.body.id}`,
|
||||||
name: res.body.displayName,
|
name: res.body.displayName,
|
||||||
imageUrl: (res.body.image.url || '').replace(/\bsz?=\d+$/, 'sz=40'),
|
imageUrl: (res.body.image.url || '').replace(/\bsz?=\d+$/, 'sz=40'),
|
||||||
});
|
});
|
||||||
|
@ -9,6 +9,7 @@ import './providers/couchdbWorkspaceProvider';
|
|||||||
import './providers/githubWorkspaceProvider';
|
import './providers/githubWorkspaceProvider';
|
||||||
import './providers/googleDriveWorkspaceProvider';
|
import './providers/googleDriveWorkspaceProvider';
|
||||||
import tempFileSvc from './tempFileSvc';
|
import tempFileSvc from './tempFileSvc';
|
||||||
|
import fileSvc from './fileSvc';
|
||||||
|
|
||||||
const minAutoSyncEvery = 60 * 1000; // 60 sec
|
const minAutoSyncEvery = 60 * 1000; // 60 sec
|
||||||
const inactivityThreshold = 3 * 1000; // 3 sec
|
const inactivityThreshold = 3 * 1000; // 3 sec
|
||||||
@ -746,7 +747,7 @@ function requestSync() {
|
|||||||
Object.entries(fileHashesToClean).forEach(([fileId, fileHash]) => {
|
Object.entries(fileHashesToClean).forEach(([fileId, fileHash]) => {
|
||||||
const file = store.state.file.itemMap[fileId];
|
const file = store.state.file.itemMap[fileId];
|
||||||
if (file && file.hash === fileHash) {
|
if (file && file.hash === fileHash) {
|
||||||
store.dispatch('deleteFile', fileId);
|
fileSvc.deleteFile(fileId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
@ -2,6 +2,7 @@ import cledit from './cledit';
|
|||||||
import store from '../store';
|
import store from '../store';
|
||||||
import utils from './utils';
|
import utils from './utils';
|
||||||
import editorSvc from './editorSvc';
|
import editorSvc from './editorSvc';
|
||||||
|
import fileSvc from './fileSvc';
|
||||||
|
|
||||||
const origin = utils.queryParams.origin;
|
const origin = utils.queryParams.origin;
|
||||||
const fileName = utils.queryParams.fileName;
|
const fileName = utils.queryParams.fileName;
|
||||||
@ -29,12 +30,12 @@ export default {
|
|||||||
|
|
||||||
store.commit('setLight', true);
|
store.commit('setLight', true);
|
||||||
|
|
||||||
return store.dispatch('createFile', {
|
return fileSvc.createFile({
|
||||||
name: fileName || utils.getHostname(origin),
|
name: fileName || utils.getHostname(origin),
|
||||||
text: contentText || '\n',
|
text: contentText || '\n',
|
||||||
properties: contentProperties,
|
properties: contentProperties,
|
||||||
parentId: 'temp',
|
parentId: 'temp',
|
||||||
})
|
}, true)
|
||||||
.then((file) => {
|
.then((file) => {
|
||||||
const fileItemMap = store.state.file.itemMap;
|
const fileItemMap = store.state.file.itemMap;
|
||||||
|
|
||||||
@ -57,7 +58,7 @@ export default {
|
|||||||
.splice(10)
|
.splice(10)
|
||||||
.forEach(([id]) => {
|
.forEach(([id]) => {
|
||||||
delete lastCreated[id];
|
delete lastCreated[id];
|
||||||
store.dispatch('deleteFile', id);
|
fileSvc.deleteFile(id);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Store file creations and open the file
|
// Store file creations and open the file
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
import googleHelper from './providers/helpers/googleHelper';
|
import googleHelper from './providers/helpers/googleHelper';
|
||||||
|
import githubHelper from './providers/helpers/githubHelper';
|
||||||
|
import utils from './utils';
|
||||||
import store from '../store';
|
import store from '../store';
|
||||||
|
|
||||||
const promised = {};
|
const promised = {};
|
||||||
|
|
||||||
|
const parseUserId = (userId) => {
|
||||||
|
const prefix = userId[2] === ':' && userId.slice(0, 2);
|
||||||
|
const type = prefix && utils.userIdPrefixes[prefix];
|
||||||
|
return type ? [type, userId.slice(3)] : ['google', userId];
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
addInfo({ id, name, imageUrl }) {
|
addInfo({ id, name, imageUrl }) {
|
||||||
promised[id] = true;
|
promised[id] = true;
|
||||||
@ -10,8 +18,10 @@ export default {
|
|||||||
},
|
},
|
||||||
getInfo(userId) {
|
getInfo(userId) {
|
||||||
if (!promised[userId]) {
|
if (!promised[userId]) {
|
||||||
|
const [type, sub] = parseUserId(userId);
|
||||||
|
|
||||||
// Try to find a token with this sub
|
// Try to find a token with this sub
|
||||||
const token = store.getters['data/googleTokens'][userId];
|
const token = store.getters[`data/${type}Tokens`][sub];
|
||||||
if (token) {
|
if (token) {
|
||||||
store.commit('userInfo/addItem', {
|
store.commit('userInfo/addItem', {
|
||||||
id: userId,
|
id: userId,
|
||||||
@ -19,10 +29,21 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user info from Google
|
// Get user info from provider
|
||||||
if (!store.state.offline) {
|
if (!store.state.offline) {
|
||||||
promised[userId] = true;
|
promised[userId] = true;
|
||||||
googleHelper.getUser(userId)
|
switch (type) {
|
||||||
|
case 'github': {
|
||||||
|
return githubHelper.getUser(sub)
|
||||||
|
.catch((err) => {
|
||||||
|
if (err.status !== 404) {
|
||||||
|
promised[userId] = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
case 'google':
|
||||||
|
default: {
|
||||||
|
return googleHelper.getUser(sub)
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (err.status !== 404) {
|
if (err.status !== 404) {
|
||||||
promised[userId] = false;
|
promised[userId] = false;
|
||||||
@ -30,5 +51,9 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -23,6 +23,7 @@ const parseQueryParams = (params) => {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// For utils.computeProperties()
|
// For utils.computeProperties()
|
||||||
const deepOverride = (obj, opt) => {
|
const deepOverride = (obj, opt) => {
|
||||||
if (obj === undefined) {
|
if (obj === undefined) {
|
||||||
@ -96,14 +97,23 @@ export default {
|
|||||||
'layoutSettings',
|
'layoutSettings',
|
||||||
'tokens',
|
'tokens',
|
||||||
],
|
],
|
||||||
|
userIdPrefixes: {
|
||||||
|
go: 'google',
|
||||||
|
gh: 'github',
|
||||||
|
},
|
||||||
textMaxLength: 250000,
|
textMaxLength: 250000,
|
||||||
sanitizeText(text) {
|
sanitizeText(text) {
|
||||||
const result = `${text || ''}`.slice(0, this.textMaxLength);
|
const result = `${text || ''}`.slice(0, this.textMaxLength);
|
||||||
// last char must be a `\n`.
|
// last char must be a `\n`.
|
||||||
return `${result}\n`.replace(/\n\n$/, '\n');
|
return `${result}\n`.replace(/\n\n$/, '\n');
|
||||||
},
|
},
|
||||||
|
defaultName: 'Untitled',
|
||||||
sanitizeName(name) {
|
sanitizeName(name) {
|
||||||
return `${name || ''}`.slice(0, 250) || 'Untitled';
|
return `${name || ''}`
|
||||||
|
// Replace `/`, control characters and other kind of spaces with a space
|
||||||
|
.replace(/[/\x00-\x1F\x7f-\xa0\s]+/g, ' ').trim()
|
||||||
|
// Keep only 250 characters
|
||||||
|
.slice(0, 250) || this.defaultName;
|
||||||
},
|
},
|
||||||
deepCopy,
|
deepCopy,
|
||||||
serializeObject(obj) {
|
serializeObject(obj) {
|
||||||
@ -124,9 +134,8 @@ export default {
|
|||||||
// If every field fits the criteria
|
// If every field fits the criteria
|
||||||
if (Object.entries(criteria).every(([key, value]) => value === item[key])) {
|
if (Object.entries(criteria).every(([key, value]) => value === item[key])) {
|
||||||
result = item;
|
result = item;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return result;
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
@ -199,6 +208,18 @@ export default {
|
|||||||
setInterval(func, interval) {
|
setInterval(func, interval) {
|
||||||
return setInterval(() => func(), this.randomize(interval));
|
return setInterval(() => func(), this.randomize(interval));
|
||||||
},
|
},
|
||||||
|
async awaitSequence(values, asyncFunc) {
|
||||||
|
const results = [];
|
||||||
|
const valuesLeft = values.slice().reverse();
|
||||||
|
const runWithNextValue = async () => {
|
||||||
|
if (!valuesLeft.length) {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
results.push(await asyncFunc(valuesLeft.pop()));
|
||||||
|
return runWithNextValue();
|
||||||
|
};
|
||||||
|
return runWithNextValue();
|
||||||
|
},
|
||||||
parseQueryParams,
|
parseQueryParams,
|
||||||
addQueryParams(url = '', params = {}, hash = false) {
|
addQueryParams(url = '', params = {}, hash = false) {
|
||||||
const keys = Object.keys(params).filter(key => params[key] != null);
|
const keys = Object.keys(params).filter(key => params[key] != null);
|
||||||
@ -239,9 +260,6 @@ export default {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
concatPaths(...paths) {
|
|
||||||
return paths.join('/').replace(/\/+/g, '/');
|
|
||||||
},
|
|
||||||
getHostname(url) {
|
getHostname(url) {
|
||||||
urlParser.href = url;
|
urlParser.href = url;
|
||||||
return urlParser.hostname;
|
return urlParser.hostname;
|
||||||
|
@ -130,7 +130,7 @@ export default {
|
|||||||
.then(() => syncSvc.requestSync())
|
.then(() => syncSvc.requestSync())
|
||||||
.then(() => dispatch('createNewDiscussion', selection)),
|
.then(() => dispatch('createNewDiscussion', selection)),
|
||||||
}, { root: true })
|
}, { root: true })
|
||||||
.catch(() => { }); // Cancel
|
.catch(() => { /* Cancel */ });
|
||||||
} else if (selection) {
|
} else if (selection) {
|
||||||
let text = rootGetters['content/current'].text.slice(selection.start, selection.end).trim();
|
let text = rootGetters['content/current'].text.slice(selection.start, selection.end).trim();
|
||||||
const maxLength = 80;
|
const maxLength = 80;
|
||||||
|
@ -190,85 +190,5 @@ export default {
|
|||||||
commit('setDragTargetId', id);
|
commit('setDragTargetId', id);
|
||||||
dispatch('openDragTarget');
|
dispatch('openDragTarget');
|
||||||
},
|
},
|
||||||
newItem({ getters, commit, dispatch }, isFolder) {
|
|
||||||
let parentId = getters.selectedNodeFolder.item.id;
|
|
||||||
if (parentId === 'trash' // Not allowed to create new items in the trash
|
|
||||||
|| (isFolder && parentId === 'temp') // Not allowed to create new folders in the temp folder
|
|
||||||
) {
|
|
||||||
parentId = null;
|
|
||||||
}
|
|
||||||
dispatch('openNode', parentId);
|
|
||||||
commit('setNewItem', {
|
|
||||||
type: isFolder ? 'folder' : 'file',
|
|
||||||
parentId,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
deleteItem({ rootState, getters, rootGetters, commit, dispatch }) {
|
|
||||||
const selectedNode = getters.selectedNode;
|
|
||||||
if (selectedNode.isNil) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
if (selectedNode.isTrash || selectedNode.item.parentId === 'trash') {
|
|
||||||
return dispatch('modal/trashDeletion', null, { root: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
// See if we have a dialog to show
|
|
||||||
let modalAction;
|
|
||||||
let moveToTrash = true;
|
|
||||||
if (selectedNode.isTemp) {
|
|
||||||
modalAction = 'modal/tempFolderDeletion';
|
|
||||||
moveToTrash = false;
|
|
||||||
} else if (selectedNode.item.parentId === 'temp') {
|
|
||||||
modalAction = 'modal/tempFileDeletion';
|
|
||||||
moveToTrash = false;
|
|
||||||
} else if (selectedNode.isFolder) {
|
|
||||||
modalAction = 'modal/folderDeletion';
|
|
||||||
}
|
|
||||||
|
|
||||||
return (modalAction
|
|
||||||
? dispatch(modalAction, selectedNode.item, { root: true })
|
|
||||||
: Promise.resolve())
|
|
||||||
.then(() => {
|
|
||||||
const deleteFile = (id) => {
|
|
||||||
if (moveToTrash) {
|
|
||||||
commit('file/patchItem', {
|
|
||||||
id,
|
|
||||||
parentId: 'trash',
|
|
||||||
}, { root: true });
|
|
||||||
} else {
|
|
||||||
dispatch('deleteFile', id, { root: true });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (selectedNode === getters.selectedNode) {
|
|
||||||
const currentFileId = rootGetters['file/current'].id;
|
|
||||||
let doClose = selectedNode.item.id === currentFileId;
|
|
||||||
if (selectedNode.isFolder) {
|
|
||||||
const recursiveDelete = (folderNode) => {
|
|
||||||
folderNode.folders.forEach(recursiveDelete);
|
|
||||||
folderNode.files.forEach((fileNode) => {
|
|
||||||
doClose = doClose || fileNode.item.id === currentFileId;
|
|
||||||
deleteFile(fileNode.item.id);
|
|
||||||
});
|
|
||||||
commit('folder/deleteItem', folderNode.item.id, { root: true });
|
|
||||||
};
|
|
||||||
recursiveDelete(selectedNode);
|
|
||||||
} else {
|
|
||||||
deleteFile(selectedNode.item.id);
|
|
||||||
}
|
|
||||||
if (doClose) {
|
|
||||||
// Close the current file by opening the last opened, not deleted one
|
|
||||||
rootGetters['data/lastOpenedIds'].some((id) => {
|
|
||||||
const file = rootState.file.itemMap[id];
|
|
||||||
if (file.parentId === 'trash') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
commit('file/setCurrentId', id, { root: true });
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, () => {}); // Cancel
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -62,6 +62,7 @@ const store = new Vuex.Store({
|
|||||||
},
|
},
|
||||||
itemPaths: (state) => {
|
itemPaths: (state) => {
|
||||||
const result = {};
|
const result = {};
|
||||||
|
const folderMap = state.folder.itemMap;
|
||||||
const getPath = (item) => {
|
const getPath = (item) => {
|
||||||
let itemPath = result[item.id];
|
let itemPath = result[item.id];
|
||||||
if (!itemPath) {
|
if (!itemPath) {
|
||||||
@ -69,29 +70,31 @@ const store = new Vuex.Store({
|
|||||||
itemPath = `.stackedit-trash/${item.name}`;
|
itemPath = `.stackedit-trash/${item.name}`;
|
||||||
} else {
|
} else {
|
||||||
let name = item.name;
|
let name = item.name;
|
||||||
if (item.type === 'folder') {
|
if (folderMap[item.id]) {
|
||||||
name += '/';
|
name += '/';
|
||||||
}
|
}
|
||||||
const parent = state.folder.itemMap[item.parentId];
|
const parentFolder = folderMap[item.parentId];
|
||||||
if (!parent) {
|
if (parentFolder) {
|
||||||
itemPath = name;
|
itemPath = getPath(parentFolder) + name;
|
||||||
} else {
|
} else {
|
||||||
itemPath = getPath(parent) + name;
|
itemPath = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result[item.id] = itemPath;
|
result[item.id] = itemPath;
|
||||||
return itemPath;
|
return itemPath;
|
||||||
};
|
};
|
||||||
|
|
||||||
[...state.folder.items, ...state.file.items].forEach(item => getPath(item));
|
[...state.folder.items, ...state.file.items].forEach(item => getPath(item));
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
pathItems: (state, getters) => {
|
pathItems: (state, getters) => {
|
||||||
const result = {};
|
const result = {};
|
||||||
const itemPaths = getters.itemPaths;
|
|
||||||
const allItemMap = getters.allItemMap;
|
const allItemMap = getters.allItemMap;
|
||||||
Object.entries(itemPaths).forEach(([id, path]) => {
|
Object.entries(getters.itemPaths).forEach(([id, path]) => {
|
||||||
result[path] = allItemMap[id];
|
const items = result[path] || [];
|
||||||
|
items.push(allItemMap[id]);
|
||||||
|
result[path] = items;
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
@ -131,33 +134,6 @@ const store = new Vuex.Store({
|
|||||||
}
|
}
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
createFile({ state, getters, commit }, desc = {}) {
|
|
||||||
const id = utils.uid();
|
|
||||||
commit('content/setItem', {
|
|
||||||
id: `${id}/content`,
|
|
||||||
text: utils.sanitizeText(desc.text || getters['data/computedSettings'].newFileContent),
|
|
||||||
properties: utils.sanitizeText(
|
|
||||||
desc.properties || getters['data/computedSettings'].newFileProperties),
|
|
||||||
discussions: desc.discussions || {},
|
|
||||||
comments: desc.comments || {},
|
|
||||||
});
|
|
||||||
commit('file/setItem', {
|
|
||||||
id,
|
|
||||||
name: utils.sanitizeName(desc.name),
|
|
||||||
parentId: desc.parentId || null,
|
|
||||||
});
|
|
||||||
return Promise.resolve(state.file.itemMap[id]);
|
|
||||||
},
|
|
||||||
deleteFile({ getters, commit }, fileId) {
|
|
||||||
(getters['syncLocation/groupedByFileId'][fileId] || [])
|
|
||||||
.forEach(item => commit('syncLocation/deleteItem', item.id));
|
|
||||||
(getters['publishLocation/groupedByFileId'][fileId] || [])
|
|
||||||
.forEach(item => commit('publishLocation/deleteItem', item.id));
|
|
||||||
commit('file/deleteItem', fileId);
|
|
||||||
commit('content/deleteItem', `${fileId}/content`);
|
|
||||||
commit('syncedContent/deleteItem', `${fileId}/syncedContent`);
|
|
||||||
commit('contentState/deleteItem', `${fileId}/contentState`);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
strict: debug,
|
strict: debug,
|
||||||
plugins: debug ? [createLogger()] : [],
|
plugins: debug ? [createLogger()] : [],
|
||||||
|
@ -74,13 +74,27 @@ export default {
|
|||||||
}),
|
}),
|
||||||
trashDeletion: ({ dispatch }) => dispatch('open', {
|
trashDeletion: ({ dispatch }) => dispatch('open', {
|
||||||
content: '<p>Files in the trash are automatically deleted after 7 days of inactivity.</p>',
|
content: '<p>Files in the trash are automatically deleted after 7 days of inactivity.</p>',
|
||||||
resolveText: 'Ok',
|
rejectText: 'Ok',
|
||||||
}),
|
}),
|
||||||
fileRestoration: ({ dispatch }) => dispatch('open', {
|
fileRestoration: ({ dispatch }) => dispatch('open', {
|
||||||
content: '<p>You are about to revert some changes. Are you sure?</p>',
|
content: '<p>You are about to revert some changes. Are you sure?</p>',
|
||||||
resolveText: 'Yes, revert',
|
resolveText: 'Yes, revert',
|
||||||
rejectText: 'No',
|
rejectText: 'No',
|
||||||
}),
|
}),
|
||||||
|
unauthorizedName: ({ dispatch }, name) => dispatch('open', {
|
||||||
|
content: `<p><b>${name}</b> is not an authorized name.</p>`,
|
||||||
|
rejectText: 'Ok',
|
||||||
|
}),
|
||||||
|
stripName: ({ dispatch }, name) => dispatch('open', {
|
||||||
|
content: `<p><b>${name}</b> contains illegal characters. Do you want to strip them?</p>`,
|
||||||
|
resolveText: 'Yes, strip',
|
||||||
|
rejectText: 'No',
|
||||||
|
}),
|
||||||
|
pathConflict: ({ dispatch }, name) => dispatch('open', {
|
||||||
|
content: `<p><b>${name}</b> already exists. Do you want to add a suffix?</p>`,
|
||||||
|
resolveText: 'Yes, add suffix',
|
||||||
|
rejectText: 'No',
|
||||||
|
}),
|
||||||
removeWorkspace: ({ dispatch }) => dispatch('open', {
|
removeWorkspace: ({ dispatch }) => dispatch('open', {
|
||||||
content: '<p>You are about to remove a workspace locally. Are you sure?</p>',
|
content: '<p>You are about to remove a workspace locally. Are you sure?</p>',
|
||||||
resolveText: 'Yes, remove',
|
resolveText: 'Yes, remove',
|
||||||
@ -127,11 +141,11 @@ export default {
|
|||||||
}),
|
}),
|
||||||
sponsorOnly: ({ dispatch }) => dispatch('open', {
|
sponsorOnly: ({ dispatch }) => dispatch('open', {
|
||||||
content: '<p>This feature is restricted to sponsors as it relies on server resources.</p>',
|
content: '<p>This feature is restricted to sponsors as it relies on server resources.</p>',
|
||||||
resolveText: 'Ok, I understand',
|
rejectText: 'Ok, I understand',
|
||||||
}),
|
}),
|
||||||
paymentSuccess: ({ dispatch }) => dispatch('open', {
|
paymentSuccess: ({ dispatch }) => dispatch('open', {
|
||||||
content: '<p>Thank you for your payment! Your sponsorship will be active in a minute.</p>',
|
content: '<p>Thank you for your payment! Your sponsorship will be active in a minute.</p>',
|
||||||
resolveText: 'Ok',
|
rejectText: 'Ok',
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -11,7 +11,10 @@ export default (empty, simpleHash = false) => {
|
|||||||
itemMap: {},
|
itemMap: {},
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
items: state => Object.entries(state.itemMap).map(([, item]) => item),
|
items: (state) => {
|
||||||
|
console.log(state.itemMap);
|
||||||
|
return Object.values(state.itemMap);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setItem(state, value) {
|
setItem(state, value) {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import utils from '../services/utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
state: {
|
state: {
|
||||||
@ -21,6 +23,10 @@ export default {
|
|||||||
const workspaces = rootGetters['data/sanitizedWorkspaces'];
|
const workspaces = rootGetters['data/sanitizedWorkspaces'];
|
||||||
return workspaces[state.currentWorkspaceId] || getters.mainWorkspace;
|
return workspaces[state.currentWorkspaceId] || getters.mainWorkspace;
|
||||||
},
|
},
|
||||||
|
hasUniquePaths: (state, getters) => {
|
||||||
|
const workspace = getters.currentWorkspace;
|
||||||
|
return workspace.providerId === 'githubWorkspace';
|
||||||
|
},
|
||||||
lastSyncActivityKey: (state, getters) => `${getters.currentWorkspace.id}/lastSyncActivity`,
|
lastSyncActivityKey: (state, getters) => `${getters.currentWorkspace.id}/lastSyncActivity`,
|
||||||
lastFocusKey: (state, getters) => `${getters.currentWorkspace.id}/lastWindowFocus`,
|
lastFocusKey: (state, getters) => `${getters.currentWorkspace.id}/lastWindowFocus`,
|
||||||
mainWorkspaceToken: (state, getters, rootState, rootGetters) => {
|
mainWorkspaceToken: (state, getters, rootState, rootGetters) => {
|
||||||
@ -55,10 +61,28 @@ export default {
|
|||||||
const googleTokens = rootGetters['data/googleTokens'];
|
const googleTokens = rootGetters['data/googleTokens'];
|
||||||
return googleTokens[workspace.sub];
|
return googleTokens[workspace.sub];
|
||||||
}
|
}
|
||||||
|
case 'githubWorkspace': {
|
||||||
|
const githubTokens = rootGetters['data/githubTokens'];
|
||||||
|
return githubTokens[workspace.sub];
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return getters.mainWorkspaceToken;
|
return getters.mainWorkspaceToken;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
userId: (state, getters, rootState, rootGetters) => {
|
||||||
|
const loginToken = getters.loginToken;
|
||||||
|
if (!loginToken) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let prefix;
|
||||||
|
Object.entries(utils.userIdPrefixes).some(([key, value]) => {
|
||||||
|
if (rootGetters[`data/${value}Tokens`][loginToken.sub]) {
|
||||||
|
prefix = key;
|
||||||
|
}
|
||||||
|
return prefix;
|
||||||
|
});
|
||||||
|
return prefix ? `${prefix}:${loginToken.sub}` : loginToken.sub;
|
||||||
|
},
|
||||||
sponsorToken: (state, getters) => getters.mainWorkspaceToken,
|
sponsorToken: (state, getters) => getters.mainWorkspaceToken,
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
Loading…
Reference in New Issue
Block a user