Temporary folder (part 2)
This commit is contained in:
parent
53f2076585
commit
88cf11972f
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="button-bar">
|
<div class="button-bar">
|
||||||
<div class="button-bar__inner button-bar__inner--top">
|
<div class="button-bar__inner button-bar__inner--top">
|
||||||
<button class="button-bar__button button" :class="{ 'button-bar__button--on': layoutSettings.showNavigationBar }" @click="toggleNavigationBar()" v-title="'Toggle navigation bar'">
|
<button class="button-bar__button button" :class="{ 'button-bar__button--on': layoutSettings.showNavigationBar }" v-if="!light" @click="toggleNavigationBar()" v-title="'Toggle navigation bar'">
|
||||||
<icon-navigation-bar></icon-navigation-bar>
|
<icon-navigation-bar></icon-navigation-bar>
|
||||||
</button>
|
</button>
|
||||||
<button class="button-bar__button button" :class="{ 'button-bar__button--on': layoutSettings.showSidePreview }" tour-step-anchor="editor" @click="toggleSidePreview()" v-title="'Toggle side preview'">
|
<button class="button-bar__button button" :class="{ 'button-bar__button--on': layoutSettings.showSidePreview }" tour-step-anchor="editor" @click="toggleSidePreview()" v-title="'Toggle side preview'">
|
||||||
@ -26,12 +26,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapState, mapGetters, mapActions } from 'vuex';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
computed: mapGetters('data', [
|
computed: {
|
||||||
'layoutSettings',
|
...mapState([
|
||||||
]),
|
'light',
|
||||||
|
]),
|
||||||
|
...mapGetters('data', [
|
||||||
|
'layoutSettings',
|
||||||
|
]),
|
||||||
|
},
|
||||||
methods: mapActions('data', [
|
methods: mapActions('data', [
|
||||||
'toggleNavigationBar',
|
'toggleNavigationBar',
|
||||||
'toggleEditor',
|
'toggleEditor',
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
<icon-close></icon-close>
|
<icon-close></icon-close>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="explorer__tree" :class="{'explorer__tree--new-item': !newChildNode.isNil}" v-if="!light" tabindex="0">
|
<div class="explorer__tree" :class="{'explorer__tree--new-item': !newChildNode.isNil}" v-if="!light" tabindex="0" @keyup.delete="deleteItem()">
|
||||||
<explorer-node :node="rootNode" :depth="0"></explorer-node>
|
<explorer-node :node="rootNode" :depth="0"></explorer-node>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -169,7 +169,7 @@ export default {
|
|||||||
perform: () => this.newItem(false),
|
perform: () => this.newItem(false),
|
||||||
}, {
|
}, {
|
||||||
name: 'New folder',
|
name: 'New folder',
|
||||||
disabled: !this.node.isFolder || this.node.isTrash,
|
disabled: !this.node.isFolder || this.node.isTrash || this.node.isTemp,
|
||||||
perform: () => this.newItem(true),
|
perform: () => this.newItem(true),
|
||||||
}, {
|
}, {
|
||||||
type: 'separator',
|
type: 'separator',
|
||||||
@ -179,7 +179,6 @@ export default {
|
|||||||
perform: () => this.setEditingId(this.node.item.id),
|
perform: () => this.setEditingId(this.node.item.id),
|
||||||
}, {
|
}, {
|
||||||
name: 'Delete',
|
name: 'Delete',
|
||||||
disabled: this.node.isTrash || this.node.item.parentId === 'trash',
|
|
||||||
perform: () => this.deleteItem(),
|
perform: () => this.deleteItem(),
|
||||||
}],
|
}],
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="modal" @keydown.esc="onEscape" @keydown.tab="onTab">
|
<div class="modal" @keydown.esc="onEscape" @keydown.tab="onTab">
|
||||||
<component :is="currentModalComponent" v-if="currentModalComponent"></component>
|
<component v-if="currentModalComponent" :is="currentModalComponent"></component>
|
||||||
<modal-inner v-else aria-label="Dialog">
|
<modal-inner v-else aria-label="Dialog">
|
||||||
<div class="modal__content" v-html="config.content"></div>
|
<div class="modal__content" v-html="config.content"></div>
|
||||||
<div class="modal__button-bar">
|
<div class="modal__button-bar">
|
||||||
@ -56,8 +56,6 @@ const getTabbables = container => container.querySelectorAll('a[href], button, .
|
|||||||
// Filter enabled and visible element
|
// Filter enabled and visible element
|
||||||
.cl_filter(el => !el.disabled && el.offsetParent !== null && !el.classList.contains('not-tabbable'));
|
.cl_filter(el => !el.disabled && el.offsetParent !== null && !el.classList.contains('not-tabbable'));
|
||||||
|
|
||||||
const kebabCase = str => str.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g, match => `-${match.toLowerCase()}`);
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ModalInner,
|
ModalInner,
|
||||||
@ -102,11 +100,15 @@ export default {
|
|||||||
'config',
|
'config',
|
||||||
]),
|
]),
|
||||||
currentModalComponent() {
|
currentModalComponent() {
|
||||||
if (!this.condig.modal) {
|
if (this.config.type) {
|
||||||
return '';
|
let componentName = this.config.type[0].toUpperCase();
|
||||||
|
componentName += this.config.type.slice(1);
|
||||||
|
componentName += 'Modal';
|
||||||
|
if (this.$options.components[componentName]) {
|
||||||
|
return componentName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
return `${kebabCase(this.config.modal)}-modal`;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -108,9 +108,7 @@ export default {
|
|||||||
}
|
}
|
||||||
this.titleFakeElt.textContent = this.title;
|
this.titleFakeElt.textContent = this.title;
|
||||||
const width = this.titleFakeElt.getBoundingClientRect().width + 2; // 2px for the caret
|
const width = this.titleFakeElt.getBoundingClientRect().width + 2; // 2px for the caret
|
||||||
return width < this.styles.titleMaxWidth
|
return Math.min(width, this.styles.titleMaxWidth);
|
||||||
? width
|
|
||||||
: this.styles.titleMaxWidth;
|
|
||||||
},
|
},
|
||||||
titleScrolling() {
|
titleScrolling() {
|
||||||
const result = this.titleHover && !this.titleFocus;
|
const result = this.titleHover && !this.titleFocus;
|
||||||
@ -220,7 +218,7 @@ export default {
|
|||||||
float: left;
|
float: left;
|
||||||
|
|
||||||
&.navigation-bar__inner--button {
|
&.navigation-bar__inner--button {
|
||||||
margin-right: 15px;
|
margin-right: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ export default {
|
|||||||
]),
|
]),
|
||||||
showSponsorButton() {
|
showSponsorButton() {
|
||||||
const type = this.$store.getters['modal/config'].type;
|
const type = this.$store.getters['modal/config'].type;
|
||||||
return !this.$store.getters.isSponsor && type !== 'sponsor' && type !== 'signInForSponsorship';
|
return !this.$store.getters.isSponsor && type !== 'SponsorModal' && type !== 'signInForSponsorship';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -5,7 +5,6 @@ import welcomeFile from '../data/welcomeFile.md';
|
|||||||
|
|
||||||
const dbVersion = 1;
|
const dbVersion = 1;
|
||||||
const dbStoreName = 'objects';
|
const dbStoreName = 'objects';
|
||||||
const fileIdToOpen = utils.queryParams.fileId;
|
|
||||||
const exportWorkspace = utils.queryParams.exportWorkspace;
|
const exportWorkspace = utils.queryParams.exportWorkspace;
|
||||||
const resetApp = utils.queryParams.reset;
|
const resetApp = utils.queryParams.reset;
|
||||||
const deleteMarkerMaxAge = 1000;
|
const deleteMarkerMaxAge = 1000;
|
||||||
@ -100,151 +99,6 @@ const localDbSvc = {
|
|||||||
hashMap,
|
hashMap,
|
||||||
connection: null,
|
connection: null,
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the connection and start syncing.
|
|
||||||
*/
|
|
||||||
init() {
|
|
||||||
return Promise.resolve()
|
|
||||||
.then(() => {
|
|
||||||
// Reset the app if reset flag was passed
|
|
||||||
if (resetApp) {
|
|
||||||
return Promise.all(
|
|
||||||
Object.keys(store.getters['data/workspaces'])
|
|
||||||
.map(workspaceId => localDbSvc.removeWorkspace(workspaceId)),
|
|
||||||
)
|
|
||||||
.then(() => utils.localStorageDataIds.forEach((id) => {
|
|
||||||
// Clean data stored in localStorage
|
|
||||||
localStorage.removeItem(`data/${id}`);
|
|
||||||
}))
|
|
||||||
.then(() => {
|
|
||||||
location.reload();
|
|
||||||
throw new Error('reload');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the connection
|
|
||||||
this.connection = new Connection();
|
|
||||||
|
|
||||||
// Load the DB
|
|
||||||
return localDbSvc.sync();
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
// If exportWorkspace parameter was provided
|
|
||||||
if (exportWorkspace) {
|
|
||||||
const backup = JSON.stringify(store.getters.allItemMap);
|
|
||||||
const blob = new Blob([backup], {
|
|
||||||
type: 'text/plain;charset=utf-8',
|
|
||||||
});
|
|
||||||
FileSaver.saveAs(blob, 'StackEdit workspace.json');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save welcome file content hash if not done already
|
|
||||||
const hash = utils.hash(welcomeFile);
|
|
||||||
const welcomeFileHashes = store.getters['data/localSettings'].welcomeFileHashes;
|
|
||||||
if (!welcomeFileHashes[hash]) {
|
|
||||||
store.dispatch('data/patchLocalSettings', {
|
|
||||||
welcomeFileHashes: {
|
|
||||||
...welcomeFileHashes,
|
|
||||||
[hash]: 1,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// If app was last opened 7 days ago and synchronization is off
|
|
||||||
if (!store.getters['workspace/syncToken'] &&
|
|
||||||
(store.state.workspace.lastFocus + utils.cleanTrashAfter < Date.now())
|
|
||||||
) {
|
|
||||||
// Clean files
|
|
||||||
store.getters['file/items']
|
|
||||||
.filter(file => file.parentId === 'trash') // If file is in the trash
|
|
||||||
.forEach(file => store.dispatch('deleteFile', file.id));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable sponsorship
|
|
||||||
if (utils.queryParams.paymentSuccess) {
|
|
||||||
location.hash = ''; // PaymentSuccess param is always on its own
|
|
||||||
store.dispatch('modal/paymentSuccess');
|
|
||||||
const sponsorToken = store.getters['workspace/sponsorToken'];
|
|
||||||
// Force check sponsorship after a few seconds
|
|
||||||
const currentDate = Date.now();
|
|
||||||
if (sponsorToken && sponsorToken.expiresOn > currentDate - checkSponsorshipAfter) {
|
|
||||||
store.dispatch('data/setGoogleToken', {
|
|
||||||
...sponsorToken,
|
|
||||||
expiresOn: currentDate - checkSponsorshipAfter,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync local DB periodically
|
|
||||||
utils.setInterval(() => localDbSvc.sync(), 1000);
|
|
||||||
|
|
||||||
if (fileIdToOpen) {
|
|
||||||
store.commit('file/setCurrentId', fileIdToOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
// watch current file changing
|
|
||||||
store.watch(
|
|
||||||
() => store.getters['file/current'].id,
|
|
||||||
() => {
|
|
||||||
// See if currentFile is real, ie it has an ID
|
|
||||||
const currentFile = store.getters['file/current'];
|
|
||||||
// If current file has no ID, get the most recent file
|
|
||||||
if (!currentFile.id) {
|
|
||||||
const recentFile = store.getters['file/lastOpened'];
|
|
||||||
// Set it as the current file
|
|
||||||
if (recentFile.id) {
|
|
||||||
store.commit('file/setCurrentId', recentFile.id);
|
|
||||||
} else {
|
|
||||||
// If still no ID, create a new file
|
|
||||||
store.dispatch('createFile', {
|
|
||||||
name: 'Welcome file',
|
|
||||||
text: welcomeFile,
|
|
||||||
})
|
|
||||||
// Set it as the current file
|
|
||||||
.then(newFile => store.commit('file/setCurrentId', newFile.id));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Promise.resolve()
|
|
||||||
// Load contentState from DB
|
|
||||||
.then(() => localDbSvc.loadContentState(currentFile.id))
|
|
||||||
// Load syncedContent from DB
|
|
||||||
.then(() => localDbSvc.loadSyncedContent(currentFile.id))
|
|
||||||
// Load content from DB
|
|
||||||
.then(() => localDbSvc.loadItem(`${currentFile.id}/content`))
|
|
||||||
.then(
|
|
||||||
() => {
|
|
||||||
// Set last opened file
|
|
||||||
store.dispatch('data/setLastOpenedId', currentFile.id);
|
|
||||||
// Cancel new discussion
|
|
||||||
store.commit('discussion/setCurrentDiscussionId');
|
|
||||||
// Open the gutter if file contains discussions
|
|
||||||
store.commit('discussion/setCurrentDiscussionId',
|
|
||||||
store.getters['discussion/nextDiscussionId']);
|
|
||||||
// Add the fileId to the queryParams
|
|
||||||
utils.setQueryParams({
|
|
||||||
...utils.queryParams,
|
|
||||||
fileId: currentFile.id,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
(err) => {
|
|
||||||
// Failure (content is not available), go back to previous file
|
|
||||||
const lastOpenedFile = store.getters['file/lastOpened'];
|
|
||||||
store.commit('file/setCurrentId', lastOpenedFile.id);
|
|
||||||
throw err;
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.catch((err) => {
|
|
||||||
console.error(err); // eslint-disable-line no-console
|
|
||||||
store.dispatch('notification/error', err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
immediate: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync data items stored in the localStorage.
|
* Sync data items stored in the localStorage.
|
||||||
*/
|
*/
|
||||||
@ -485,6 +339,142 @@ const localDbSvc = {
|
|||||||
localStorage.removeItem(`${id}/lastWindowFocus`);
|
localStorage.removeItem(`${id}/lastWindowFocus`);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the connection and start syncing.
|
||||||
|
*/
|
||||||
|
init() {
|
||||||
|
return Promise.resolve()
|
||||||
|
.then(() => {
|
||||||
|
// Reset the app if reset flag was passed
|
||||||
|
if (resetApp) {
|
||||||
|
return Promise.all(
|
||||||
|
Object.keys(store.getters['data/workspaces'])
|
||||||
|
.map(workspaceId => localDbSvc.removeWorkspace(workspaceId)),
|
||||||
|
)
|
||||||
|
.then(() => utils.localStorageDataIds.forEach((id) => {
|
||||||
|
// Clean data stored in localStorage
|
||||||
|
localStorage.removeItem(`data/${id}`);
|
||||||
|
}))
|
||||||
|
.then(() => {
|
||||||
|
location.reload();
|
||||||
|
throw new Error('reload');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the connection
|
||||||
|
this.connection = new Connection();
|
||||||
|
|
||||||
|
// Load the DB
|
||||||
|
return localDbSvc.sync();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// If exportWorkspace parameter was provided
|
||||||
|
if (exportWorkspace) {
|
||||||
|
const backup = JSON.stringify(store.getters.allItemMap);
|
||||||
|
const blob = new Blob([backup], {
|
||||||
|
type: 'text/plain;charset=utf-8',
|
||||||
|
});
|
||||||
|
FileSaver.saveAs(blob, 'StackEdit workspace.json');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save welcome file content hash if not done already
|
||||||
|
const hash = utils.hash(welcomeFile);
|
||||||
|
const welcomeFileHashes = store.getters['data/localSettings'].welcomeFileHashes;
|
||||||
|
if (!welcomeFileHashes[hash]) {
|
||||||
|
store.dispatch('data/patchLocalSettings', {
|
||||||
|
welcomeFileHashes: {
|
||||||
|
...welcomeFileHashes,
|
||||||
|
[hash]: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If app was last opened 7 days ago and synchronization is off
|
||||||
|
if (!store.getters['workspace/syncToken'] &&
|
||||||
|
(store.state.workspace.lastFocus + utils.cleanTrashAfter < Date.now())
|
||||||
|
) {
|
||||||
|
// Clean files
|
||||||
|
store.getters['file/items']
|
||||||
|
.filter(file => file.parentId === 'trash') // If file is in the trash
|
||||||
|
.forEach(file => store.dispatch('deleteFile', file.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable sponsorship
|
||||||
|
if (utils.queryParams.paymentSuccess) {
|
||||||
|
location.hash = ''; // PaymentSuccess param is always on its own
|
||||||
|
store.dispatch('modal/paymentSuccess');
|
||||||
|
const sponsorToken = store.getters['workspace/sponsorToken'];
|
||||||
|
// Force check sponsorship after a few seconds
|
||||||
|
const currentDate = Date.now();
|
||||||
|
if (sponsorToken && sponsorToken.expiresOn > currentDate - checkSponsorshipAfter) {
|
||||||
|
store.dispatch('data/setGoogleToken', {
|
||||||
|
...sponsorToken,
|
||||||
|
expiresOn: currentDate - checkSponsorshipAfter,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync local DB periodically
|
||||||
|
utils.setInterval(() => localDbSvc.sync(), 1000);
|
||||||
|
|
||||||
|
// watch current file changing
|
||||||
|
store.watch(
|
||||||
|
() => store.getters['file/current'].id,
|
||||||
|
() => {
|
||||||
|
// See if currentFile is real, ie it has an ID
|
||||||
|
const currentFile = store.getters['file/current'];
|
||||||
|
// If current file has no ID, get the most recent file
|
||||||
|
if (!currentFile.id) {
|
||||||
|
const recentFile = store.getters['file/lastOpened'];
|
||||||
|
// Set it as the current file
|
||||||
|
if (recentFile.id) {
|
||||||
|
store.commit('file/setCurrentId', recentFile.id);
|
||||||
|
} else {
|
||||||
|
// If still no ID, create a new file
|
||||||
|
store.dispatch('createFile', {
|
||||||
|
name: 'Welcome file',
|
||||||
|
text: welcomeFile,
|
||||||
|
})
|
||||||
|
// Set it as the current file
|
||||||
|
.then(newFile => store.commit('file/setCurrentId', newFile.id));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Promise.resolve()
|
||||||
|
// Load contentState from DB
|
||||||
|
.then(() => localDbSvc.loadContentState(currentFile.id))
|
||||||
|
// Load syncedContent from DB
|
||||||
|
.then(() => localDbSvc.loadSyncedContent(currentFile.id))
|
||||||
|
// Load content from DB
|
||||||
|
.then(() => localDbSvc.loadItem(`${currentFile.id}/content`))
|
||||||
|
.then(
|
||||||
|
() => {
|
||||||
|
// Set last opened file
|
||||||
|
store.dispatch('data/setLastOpenedId', currentFile.id);
|
||||||
|
// Cancel new discussion
|
||||||
|
store.commit('discussion/setCurrentDiscussionId');
|
||||||
|
// Open the gutter if file contains discussions
|
||||||
|
store.commit('discussion/setCurrentDiscussionId',
|
||||||
|
store.getters['discussion/nextDiscussionId']);
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
// Failure (content is not available), go back to previous file
|
||||||
|
const lastOpenedFile = store.getters['file/lastOpened'];
|
||||||
|
store.commit('file/setCurrentId', lastOpenedFile.id);
|
||||||
|
throw err;
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err); // eslint-disable-line no-console
|
||||||
|
store.dispatch('notification/error', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
immediate: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const loader = type => fileId => localDbSvc.loadItem(`${fileId}/${type}`)
|
const loader = type => fileId => localDbSvc.loadItem(`${fileId}/${type}`)
|
||||||
|
@ -4,16 +4,17 @@ import utils from './utils';
|
|||||||
import editorSvc from './editorSvc';
|
import editorSvc from './editorSvc';
|
||||||
|
|
||||||
const origin = utils.queryParams.origin;
|
const origin = utils.queryParams.origin;
|
||||||
const existingFileId = utils.queryParams.fileId;
|
|
||||||
const fileName = utils.queryParams.fileName;
|
const fileName = utils.queryParams.fileName;
|
||||||
const contentText = utils.queryParams.contentText;
|
const contentText = utils.queryParams.contentText;
|
||||||
const contentProperties = utils.queryParams.contentProperties;
|
const contentProperties = utils.queryParams.contentProperties;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
closed: false,
|
||||||
close() {
|
close() {
|
||||||
if (origin && window.parent) {
|
if (!this.closed && origin && window.parent) {
|
||||||
window.parent.postMessage({ type: 'close' }, origin);
|
window.parent.postMessage({ type: 'close' }, origin);
|
||||||
}
|
}
|
||||||
|
this.closed = true;
|
||||||
},
|
},
|
||||||
init() {
|
init() {
|
||||||
if (!origin || !window.parent) {
|
if (!origin || !window.parent) {
|
||||||
@ -21,64 +22,39 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
store.commit('setLight', true);
|
store.commit('setLight', true);
|
||||||
return Promise.resolve()
|
return store.dispatch('createFile', {
|
||||||
.then(() => {
|
name: fileName,
|
||||||
const file = store.state.file.itemMap[existingFileId];
|
text: contentText,
|
||||||
if (file) {
|
properties: contentProperties,
|
||||||
// If file exists, check that the origin site has created it
|
parentId: 'temp',
|
||||||
const fileCreation = store.getters['data/fileCreations'][file.id];
|
})
|
||||||
if (fileCreation && fileCreation.origin === origin) {
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new temp file
|
|
||||||
return store.dispatch('createFile', {
|
|
||||||
name: fileName,
|
|
||||||
text: contentText,
|
|
||||||
properties: contentProperties,
|
|
||||||
parentId: 'temp',
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then((file) => {
|
.then((file) => {
|
||||||
const fileItemMap = store.state.file.itemMap;
|
const fileItemMap = store.state.file.itemMap;
|
||||||
|
|
||||||
// Sanitize file creations
|
// Sanitize file creations
|
||||||
const fileCreations = {};
|
const lastCreated = {};
|
||||||
Object.entries(store.getters['data/fileCreations']).forEach(([id, fileCreation]) => {
|
Object.entries(store.getters['data/lastCreated']).forEach(([id, createdOn]) => {
|
||||||
if (fileItemMap[id]) {
|
if (fileItemMap[id] && fileItemMap[id].parentId === 'temp') {
|
||||||
fileCreations[id] = fileCreation;
|
lastCreated[id] = createdOn;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Track file creation from the origin site
|
// Track file creation from other site
|
||||||
fileCreations[file.id] = {
|
lastCreated[file.id] = {
|
||||||
created: Date.now(),
|
created: Date.now(),
|
||||||
origin,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// List temp files
|
// Keep only the last 10 temp files created by other sites
|
||||||
const tempFileCreations = [];
|
Object.entries(lastCreated)
|
||||||
Object.entries(fileCreations).forEach(([id, fileCreation]) => {
|
.sort(([, createdOn1], [, createdOn2]) => createdOn2 - createdOn1)
|
||||||
if (fileItemMap[id].parentId === 'temp') {
|
|
||||||
tempFileCreations.push({
|
|
||||||
id,
|
|
||||||
created: fileCreation.created,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Keep only the last 10 temp files
|
|
||||||
tempFileCreations
|
|
||||||
.sort((fileCreation1, fileCreation2) => fileCreation2.created - fileCreation1.created)
|
|
||||||
.splice(10)
|
.splice(10)
|
||||||
.forEach((fileCreation) => {
|
.forEach(([id]) => {
|
||||||
delete fileCreations[fileCreation.id];
|
delete lastCreated[id];
|
||||||
store.dispatch('deleteFile', fileCreation.id);
|
store.dispatch('deleteFile', id);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Store file creations and open the file
|
// Store file creations and open the file
|
||||||
store.dispatch('data/setFileCreations', fileCreations);
|
store.dispatch('data/setLastCreated', lastCreated);
|
||||||
store.commit('file/setCurrentId', file.id);
|
store.commit('file/setCurrentId', file.id);
|
||||||
|
|
||||||
const onChange = cledit.Utils.debounce(() => {
|
const onChange = cledit.Utils.debounce(() => {
|
||||||
@ -86,7 +62,7 @@ export default {
|
|||||||
if (currentFile.id !== file.id) {
|
if (currentFile.id !== file.id) {
|
||||||
// Close editor if file has changed for some reason
|
// Close editor if file has changed for some reason
|
||||||
this.close();
|
this.close();
|
||||||
} else if (editorSvc.previewCtx.html != null) {
|
} else if (!this.closed && editorSvc.previewCtx.html != null) {
|
||||||
const content = store.getters['content/current'];
|
const content = store.getters['content/current'];
|
||||||
const properties = utils.computeProperties(content.properties);
|
const properties = utils.computeProperties(content.properties);
|
||||||
window.parent.postMessage({
|
window.parent.postMessage({
|
||||||
|
@ -170,7 +170,7 @@ export default {
|
|||||||
...getters.templates,
|
...getters.templates,
|
||||||
...additionalTemplates,
|
...additionalTemplates,
|
||||||
}),
|
}),
|
||||||
fileCreations: getter('fileCreations'),
|
lastCreated: getter('lastCreated'),
|
||||||
lastOpened: getter('lastOpened'),
|
lastOpened: getter('lastOpened'),
|
||||||
lastOpenedIds: (state, getters, rootState) => {
|
lastOpenedIds: (state, getters, rootState) => {
|
||||||
const lastOpened = {
|
const lastOpened = {
|
||||||
@ -262,7 +262,7 @@ export default {
|
|||||||
});
|
});
|
||||||
commit('setItem', itemTemplate('templates', dataToCommit));
|
commit('setItem', itemTemplate('templates', dataToCommit));
|
||||||
},
|
},
|
||||||
setFileCreations: setter('fileCreations'),
|
setLastCreated: setter('lastCreated'),
|
||||||
setLastOpenedId: ({ getters, commit, dispatch, rootState }, fileId) => {
|
setLastOpenedId: ({ getters, commit, dispatch, rootState }, fileId) => {
|
||||||
const lastOpened = { ...getters.lastOpened };
|
const lastOpened = { ...getters.lastOpened };
|
||||||
lastOpened[fileId] = Date.now();
|
lastOpened[fileId] = Date.now();
|
||||||
|
@ -69,17 +69,26 @@ export default {
|
|||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
nodeStructure: (state, getters, rootState, rootGetters) => {
|
nodeStructure: (state, getters, rootState, rootGetters) => {
|
||||||
|
const rootNode = new Node(emptyFolder(), [], true, true);
|
||||||
|
|
||||||
|
// Create Trash node
|
||||||
const trashFolderNode = new Node(emptyFolder(), [], true);
|
const trashFolderNode = new Node(emptyFolder(), [], true);
|
||||||
trashFolderNode.item.id = 'trash';
|
trashFolderNode.item.id = 'trash';
|
||||||
trashFolderNode.item.name = 'Trash';
|
trashFolderNode.item.name = 'Trash';
|
||||||
trashFolderNode.noDrag = true;
|
trashFolderNode.noDrag = true;
|
||||||
trashFolderNode.isTrash = true;
|
trashFolderNode.isTrash = true;
|
||||||
|
trashFolderNode.parentNode = rootNode;
|
||||||
|
|
||||||
|
// Create Temp node
|
||||||
const tempFolderNode = new Node(emptyFolder(), [], true);
|
const tempFolderNode = new Node(emptyFolder(), [], true);
|
||||||
tempFolderNode.item.id = 'temp';
|
tempFolderNode.item.id = 'temp';
|
||||||
tempFolderNode.item.name = 'Temp';
|
tempFolderNode.item.name = 'Temp';
|
||||||
tempFolderNode.noDrag = true;
|
tempFolderNode.noDrag = true;
|
||||||
tempFolderNode.noDrop = true;
|
tempFolderNode.noDrop = true;
|
||||||
tempFolderNode.isTemp = true;
|
tempFolderNode.isTemp = true;
|
||||||
|
tempFolderNode.parentNode = rootNode;
|
||||||
|
|
||||||
|
// Fill nodeMap with all file and folder nodes
|
||||||
const nodeMap = {
|
const nodeMap = {
|
||||||
trash: trashFolderNode,
|
trash: trashFolderNode,
|
||||||
temp: tempFolderNode,
|
temp: tempFolderNode,
|
||||||
@ -96,7 +105,8 @@ export default {
|
|||||||
];
|
];
|
||||||
nodeMap[item.id] = new Node(item, locations);
|
nodeMap[item.id] = new Node(item, locations);
|
||||||
});
|
});
|
||||||
const rootNode = new Node(emptyFolder(), [], true, true);
|
|
||||||
|
// Build the tree
|
||||||
Object.entries(nodeMap).forEach(([, node]) => {
|
Object.entries(nodeMap).forEach(([, node]) => {
|
||||||
let parentNode = nodeMap[node.item.parentId];
|
let parentNode = nodeMap[node.item.parentId];
|
||||||
if (!parentNode || !parentNode.isFolder) {
|
if (!parentNode || !parentNode.isFolder) {
|
||||||
@ -110,8 +120,11 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
parentNode.files.push(node);
|
parentNode.files.push(node);
|
||||||
}
|
}
|
||||||
|
node.parentNode = parentNode;
|
||||||
});
|
});
|
||||||
rootNode.sortChildren();
|
rootNode.sortChildren();
|
||||||
|
|
||||||
|
// Add Trash and Temp nodes
|
||||||
rootNode.folders.unshift(tempFolderNode);
|
rootNode.folders.unshift(tempFolderNode);
|
||||||
tempFolderNode.files.forEach((node) => {
|
tempFolderNode.files.forEach((node) => {
|
||||||
node.noDrop = true;
|
node.noDrop = true;
|
||||||
@ -119,7 +132,8 @@ export default {
|
|||||||
if (trashFolderNode.files.length) {
|
if (trashFolderNode.files.length) {
|
||||||
rootNode.folders.unshift(trashFolderNode);
|
rootNode.folders.unshift(trashFolderNode);
|
||||||
}
|
}
|
||||||
// Add a fake file at the end of the root folder to allow drag and drop into it.
|
|
||||||
|
// Add a fake file at the end of the root folder to allow drag and drop into it
|
||||||
rootNode.files.push(fakeFileNode);
|
rootNode.files.push(fakeFileNode);
|
||||||
return {
|
return {
|
||||||
nodeMap,
|
nodeMap,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const minPadding = 20;
|
const minPadding = 20;
|
||||||
const editorTopPadding = 10;
|
const editorTopPadding = 10;
|
||||||
const navigationBarEditButtonsWidth = (36 * 14) + 8; // 14 buttons + 1 spacer
|
const navigationBarEditButtonsWidth = (36 * 14) + 8; // 14 buttons + 1 spacer
|
||||||
const navigationBarLeftButtonWidth = 38 + 4 + 15;
|
const navigationBarLeftButtonWidth = 38 + 4 + 12;
|
||||||
const navigationBarRightButtonWidth = 38 + 8;
|
const navigationBarRightButtonWidth = 38 + 8;
|
||||||
const navigationBarSpinnerWidth = 24 + 8 + 5; // 5 for left margin
|
const navigationBarSpinnerWidth = 24 + 8 + 5; // 5 for left margin
|
||||||
const navigationBarLocationWidth = 20;
|
const navigationBarLocationWidth = 20;
|
||||||
@ -23,7 +23,8 @@ const constants = {
|
|||||||
function computeStyles(state, getters, layoutSettings = getters['data/layoutSettings'], styles = {
|
function computeStyles(state, getters, layoutSettings = getters['data/layoutSettings'], styles = {
|
||||||
showNavigationBar: layoutSettings.showNavigationBar
|
showNavigationBar: layoutSettings.showNavigationBar
|
||||||
|| !layoutSettings.showEditor
|
|| !layoutSettings.showEditor
|
||||||
|| state.content.revisionContent,
|
|| state.content.revisionContent
|
||||||
|
|| state.light,
|
||||||
showStatusBar: layoutSettings.showStatusBar,
|
showStatusBar: layoutSettings.showStatusBar,
|
||||||
showEditor: layoutSettings.showEditor,
|
showEditor: layoutSettings.showEditor,
|
||||||
showSidePreview: layoutSettings.showSidePreview && layoutSettings.showEditor,
|
showSidePreview: layoutSettings.showSidePreview && layoutSettings.showEditor,
|
||||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
Loading…
Reference in New Issue
Block a user