Revision history
This commit is contained in:
parent
815c87b5e3
commit
6b6ef52bb4
@ -21,6 +21,10 @@ import store from '../store';
|
|||||||
Vue.directive('focus', {
|
Vue.directive('focus', {
|
||||||
inserted(el) {
|
inserted(el) {
|
||||||
el.focus();
|
el.focus();
|
||||||
|
const value = el.value;
|
||||||
|
if (value && el.setSelectionRange) {
|
||||||
|
el.setSelectionRange(0, value.length);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -16,11 +16,11 @@ export default {
|
|||||||
}
|
}
|
||||||
if (scrollElt) {
|
if (scrollElt) {
|
||||||
const clEditor = cledit(preElt, scrollElt);
|
const clEditor = cledit(preElt, scrollElt);
|
||||||
|
clEditor.on('contentChanged', value => this.$emit('changed', value));
|
||||||
clEditor.init({
|
clEditor.init({
|
||||||
content: this.value,
|
content: this.value,
|
||||||
sectionHighlighter: section => Prism.highlight(section.text, Prism.languages[this.lang]),
|
sectionHighlighter: section => Prism.highlight(section.text, Prism.languages[this.lang]),
|
||||||
});
|
});
|
||||||
clEditor.on('contentChanged', value => this.$emit('changed', value));
|
|
||||||
clEditor.toggleEditable(!this.disabled);
|
clEditor.toggleEditable(!this.disabled);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout">
|
<div class="layout" :class="{'layout--revision': revisionContent}">
|
||||||
<div class="layout__panel flex flex--row" :class="{'flex--end': styles.showSideBar}">
|
<div class="layout__panel flex flex--row" :class="{'flex--end': styles.showSideBar}">
|
||||||
<div class="layout__panel layout__panel--explorer" v-show="styles.showExplorer" :aria-hidden="!styles.showExplorer" :style="{width: styles.layoutOverflow ? '100%' : constants.explorerWidth + 'px'}">
|
<div class="layout__panel layout__panel--explorer" v-show="styles.showExplorer" :aria-hidden="!styles.showExplorer" :style="{width: styles.layoutOverflow ? '100%' : constants.explorerWidth + 'px'}">
|
||||||
<explorer></explorer>
|
<explorer></explorer>
|
||||||
@ -75,6 +75,9 @@ export default {
|
|||||||
FindReplace,
|
FindReplace,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
...mapState('content', [
|
||||||
|
'revisionContent',
|
||||||
|
]),
|
||||||
...mapState('discussion', [
|
...mapState('discussion', [
|
||||||
'stickyComment',
|
'stickyComment',
|
||||||
]),
|
]),
|
||||||
@ -155,11 +158,6 @@ $editor-background: #fff;
|
|||||||
.sticky-comment,
|
.sticky-comment,
|
||||||
.current-discussion {
|
.current-discussion {
|
||||||
background-color: mix(#000, $editor-background, 6.7%);
|
background-color: mix(#000, $editor-background, 6.7%);
|
||||||
}
|
|
||||||
|
|
||||||
.comment-list__current-discussion,
|
|
||||||
.sticky-comment,
|
|
||||||
.current-discussion {
|
|
||||||
border-color: $editor-background;
|
border-color: $editor-background;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -177,11 +175,6 @@ $preview-background: #f3f3f3;
|
|||||||
.sticky-comment,
|
.sticky-comment,
|
||||||
.current-discussion {
|
.current-discussion {
|
||||||
background-color: mix(#000, $preview-background, 6.7%);
|
background-color: mix(#000, $preview-background, 6.7%);
|
||||||
}
|
|
||||||
|
|
||||||
.comment-list__current-discussion,
|
|
||||||
.sticky-comment,
|
|
||||||
.current-discussion {
|
|
||||||
border-color: $preview-background;
|
border-color: $preview-background;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,7 +189,7 @@ export default {
|
|||||||
|
|
||||||
.modal__inner-2 {
|
.modal__inner-2 {
|
||||||
margin: 40px 10px 100px;
|
margin: 40px 10px 100px;
|
||||||
background-color: #fff;
|
background-color: #f8f8f8;
|
||||||
padding: 40px 50px 30px;
|
padding: 40px 50px 30px;
|
||||||
border-radius: $border-radius-base;
|
border-radius: $border-radius-base;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -1,88 +1,58 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav class="navigation-bar" :class="{'navigation-bar--editor': styles.showEditor}">
|
<nav class="navigation-bar" :class="{'navigation-bar--editor': styles.showEditor && !revisionContent}">
|
||||||
|
<!-- Explorer -->
|
||||||
<div class="navigation-bar__inner navigation-bar__inner--left navigation-bar__inner--button">
|
<div class="navigation-bar__inner navigation-bar__inner--left navigation-bar__inner--button">
|
||||||
<button class="navigation-bar__button button" @click="toggleExplorer()" v-title="'Toggle explorer'">
|
<button class="navigation-bar__button button" @click="toggleExplorer()" v-title="'Toggle explorer'"><icon-folder></icon-folder></button>
|
||||||
<icon-folder></icon-folder>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Side bar -->
|
||||||
<div class="navigation-bar__inner navigation-bar__inner--right navigation-bar__inner--button">
|
<div class="navigation-bar__inner navigation-bar__inner--right navigation-bar__inner--button">
|
||||||
<button class="navigation-bar__button navigation-bar__button--stackedit button" @click="toggleSideBar()" v-title="'Toggle side bar'">
|
<button class="navigation-bar__button navigation-bar__button--stackedit button" @click="toggleSideBar()" v-title="'Toggle side bar'"><icon-provider provider-id="stackedit"></icon-provider></button>
|
||||||
<icon-provider provider-id="stackedit"></icon-provider>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="navigation-bar__inner navigation-bar__inner--right navigation-bar__inner--title flex flex--row">
|
<div class="navigation-bar__inner navigation-bar__inner--right navigation-bar__inner--title flex flex--row">
|
||||||
|
<!-- Spinner -->
|
||||||
<div class="navigation-bar__spinner">
|
<div class="navigation-bar__spinner">
|
||||||
<div v-if="!offline && showSpinner" class="spinner"></div>
|
<div v-if="!offline && showSpinner" class="spinner"></div>
|
||||||
<icon-sync-off v-if="offline"></icon-sync-off>
|
<icon-sync-off v-if="offline"></icon-sync-off>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 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)" @keyup.enter="submitTitle()" @keyup.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)" @keyup.enter="submitTitle()" @keyup.esc="submitTitle(true)" @mouseenter="titleHover = true" @mouseleave="titleHover = false" v-model="title">
|
||||||
|
<!-- 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'">
|
<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>
|
||||||
<icon-provider :provider-id="location.providerId"></icon-provider>
|
<button class="navigation-bar__button navigation-bar__button--sync button" :disabled="!isSyncPossible || isSyncRequested || offline" @click="requestSync" v-title="'Synchronize now'"><icon-sync></icon-sync></button>
|
||||||
</a>
|
<a class="navigation-bar__button navigation-bar__button--location button" :class="{'navigation-bar__button--blink': location.id === currentLocation.id}" v-for="location in publishLocations" :key="location.id" :href="location.url" target="_blank" v-title="'Publish location'"><icon-provider :provider-id="location.providerId"></icon-provider></a>
|
||||||
<button class="navigation-bar__button navigation-bar__button--sync button" :disabled="!isSyncPossible || isSyncRequested || offline" @click="requestSync" v-title="'Synchronize now'">
|
<button class="navigation-bar__button navigation-bar__button--publish button" :disabled="!publishLocations.length || isPublishRequested || offline" @click="requestPublish"v-title="'Publish now'"><icon-upload></icon-upload></button>
|
||||||
<icon-sync></icon-sync>
|
</div>
|
||||||
</button>
|
<!-- Revision -->
|
||||||
<a class="navigation-bar__button navigation-bar__button--location button" :class="{'navigation-bar__button--blink': location.id === currentLocation.id}" v-for="location in publishLocations" :key="location.id" :href="location.url" target="_blank" v-title="'Publish location'">
|
<div class="flex flex--row" v-if="revisionContent">
|
||||||
<icon-provider :provider-id="location.providerId"></icon-provider>
|
<button class="navigation-bar__button navigation-bar__button--revision navigation-bar__button--restore button" @click="restoreRevision">Restore</button>
|
||||||
</a>
|
<button class="navigation-bar__button navigation-bar__button--revision button" @click="setRevisionContent()" v-title="'Close revision'"><icon-close></icon-close></button>
|
||||||
<button class="navigation-bar__button navigation-bar__button--publish button" :disabled="!publishLocations.length || isPublishRequested || offline" @click="requestPublish"v-title="'Publish now'">
|
|
||||||
<icon-upload></icon-upload>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="navigation-bar__inner navigation-bar__inner--edit-buttons">
|
<div class="navigation-bar__inner navigation-bar__inner--edit-buttons">
|
||||||
<button class="navigation-bar__button button" @click="undo" v-title="'Undo'" :disabled="!canUndo">
|
<button class="navigation-bar__button button" @click="undo" v-title="'Undo'" :disabled="!canUndo"><icon-undo></icon-undo></button>
|
||||||
<icon-undo></icon-undo>
|
<button class="navigation-bar__button button" @click="redo" v-title="'Redo'" :disabled="!canRedo"><icon-redo></icon-redo></button>
|
||||||
</button>
|
|
||||||
<button class="navigation-bar__button button" @click="redo" v-title="'Redo'" :disabled="!canRedo">
|
|
||||||
<icon-redo></icon-redo>
|
|
||||||
</button>
|
|
||||||
<div class="navigation-bar__spacer"></div>
|
<div class="navigation-bar__spacer"></div>
|
||||||
<button class="navigation-bar__button button" @click="pagedownClick('bold')" v-title="'Bold'">
|
<button class="navigation-bar__button button" @click="pagedownClick('bold')" v-title="'Bold'"><icon-format-bold></icon-format-bold></button>
|
||||||
<icon-format-bold></icon-format-bold>
|
<button class="navigation-bar__button button" @click="pagedownClick('italic')" v-title="'Italic'"><icon-format-italic></icon-format-italic></button>
|
||||||
</button>
|
<button class="navigation-bar__button button" @click="pagedownClick('strikethrough')" v-title="'Strikethrough'"><icon-format-strikethrough></icon-format-strikethrough></button>
|
||||||
<button class="navigation-bar__button button" @click="pagedownClick('italic')" v-title="'Italic'">
|
<button class="navigation-bar__button button" @click="pagedownClick('heading')" v-title="'Heading'"><icon-format-size></icon-format-size></button>
|
||||||
<icon-format-italic></icon-format-italic>
|
<button class="navigation-bar__button button" @click="pagedownClick('ulist')" v-title="'Unordered list'"><icon-format-list-bulleted></icon-format-list-bulleted></button>
|
||||||
</button>
|
<button class="navigation-bar__button button" @click="pagedownClick('olist')" v-title="'Ordered list'"><icon-format-list-numbers></icon-format-list-numbers></button>
|
||||||
<button class="navigation-bar__button button" @click="pagedownClick('strikethrough')" v-title="'Strikethrough'">
|
<button class="navigation-bar__button button" @click="pagedownClick('table')" v-title="'Table'"><icon-table></icon-table></button>
|
||||||
<icon-format-strikethrough></icon-format-strikethrough>
|
<button class="navigation-bar__button button" @click="pagedownClick('quote')" v-title="'Blockquote'"><icon-format-quote-close></icon-format-quote-close></button>
|
||||||
</button>
|
<button class="navigation-bar__button button" @click="pagedownClick('code')" v-title="'Code'"><icon-code-tags></icon-code-tags></button>
|
||||||
<button class="navigation-bar__button button" @click="pagedownClick('heading')" v-title="'Heading'">
|
<button class="navigation-bar__button button" @click="pagedownClick('link')" v-title="'Link'"><icon-link-variant></icon-link-variant></button>
|
||||||
<icon-format-size></icon-format-size>
|
<button class="navigation-bar__button button" @click="pagedownClick('image')" v-title="'Image'"><icon-file-image></icon-file-image></button>
|
||||||
</button>
|
<button class="navigation-bar__button button" @click="pagedownClick('hr')" v-title="'Horizontal rule'"><icon-format-horizontal-rule></icon-format-horizontal-rule></button>
|
||||||
<button class="navigation-bar__button button" @click="pagedownClick('ulist')" v-title="'Unordered list'">
|
|
||||||
<icon-format-list-bulleted></icon-format-list-bulleted>
|
|
||||||
</button>
|
|
||||||
<button class="navigation-bar__button button" @click="pagedownClick('olist')" v-title="'Ordered list'">
|
|
||||||
<icon-format-list-numbers></icon-format-list-numbers>
|
|
||||||
</button>
|
|
||||||
<button class="navigation-bar__button button" @click="pagedownClick('table')" v-title="'Table'">
|
|
||||||
<icon-table></icon-table>
|
|
||||||
</button>
|
|
||||||
<button class="navigation-bar__button button" @click="pagedownClick('quote')" v-title="'Blockquote'">
|
|
||||||
<icon-format-quote-close></icon-format-quote-close>
|
|
||||||
</button>
|
|
||||||
<button class="navigation-bar__button button" @click="pagedownClick('code')" v-title="'Code'">
|
|
||||||
<icon-code-tags></icon-code-tags>
|
|
||||||
</button>
|
|
||||||
<button class="navigation-bar__button button" @click="pagedownClick('link')" v-title="'Link'">
|
|
||||||
<icon-link-variant></icon-link-variant>
|
|
||||||
</button>
|
|
||||||
<button class="navigation-bar__button button" @click="pagedownClick('image')" v-title="'Image'">
|
|
||||||
<icon-file-image></icon-file-image>
|
|
||||||
</button>
|
|
||||||
<button class="navigation-bar__button button" @click="pagedownClick('hr')" v-title="'Horizontal rule'">
|
|
||||||
<icon-format-horizontal-rule></icon-format-horizontal-rule>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapGetters, mapActions } from 'vuex';
|
import { mapState, mapMutations, mapGetters, mapActions } from 'vuex';
|
||||||
import editorSvc from '../services/editorSvc';
|
import editorSvc from '../services/editorSvc';
|
||||||
import syncSvc from '../services/syncSvc';
|
import syncSvc from '../services/syncSvc';
|
||||||
import publishSvc from '../services/publishSvc';
|
import publishSvc from '../services/publishSvc';
|
||||||
@ -109,6 +79,9 @@ export default {
|
|||||||
'canUndo',
|
'canUndo',
|
||||||
'canRedo',
|
'canRedo',
|
||||||
]),
|
]),
|
||||||
|
...mapState('content', [
|
||||||
|
'revisionContent',
|
||||||
|
]),
|
||||||
...mapGetters('layout', [
|
...mapGetters('layout', [
|
||||||
'styles',
|
'styles',
|
||||||
]),
|
]),
|
||||||
@ -155,6 +128,12 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
...mapMutations('content', [
|
||||||
|
'setRevisionContent',
|
||||||
|
]),
|
||||||
|
...mapActions('content', [
|
||||||
|
'restoreRevision',
|
||||||
|
]),
|
||||||
...mapActions('data', [
|
...mapActions('data', [
|
||||||
'toggleExplorer',
|
'toggleExplorer',
|
||||||
'toggleSideBar',
|
'toggleSideBar',
|
||||||
@ -176,9 +155,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
pagedownClick(name) {
|
pagedownClick(name) {
|
||||||
if (this.$store.getters['content/current'].id &&
|
if (this.$store.getters['content/isCurrentEditable']) {
|
||||||
this.$store.getters['layout/styles'].showEditor
|
|
||||||
) {
|
|
||||||
editorSvc.pagedownEditor.uiManager.doClick(name);
|
editorSvc.pagedownEditor.uiManager.doClick(name);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -292,8 +269,30 @@ $button-size: 36px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navigation-bar__button--revision {
|
||||||
|
width: 38px;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation-bar__button--restore {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.navigation-bar__title {
|
.navigation-bar__title {
|
||||||
margin: 0 4px;
|
margin: 0 4px;
|
||||||
|
font-size: 22px;
|
||||||
|
|
||||||
|
.layout--revision & {
|
||||||
|
position: absolute;
|
||||||
|
left: -9999px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.navigation-bar__title,
|
.navigation-bar__title,
|
||||||
@ -301,7 +300,6 @@ $button-size: 36px;
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
color: $navbar-color;
|
color: $navbar-color;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
font-size: 22px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.navigation-bar__button--sync,
|
.navigation-bar__button--sync,
|
||||||
@ -385,6 +383,10 @@ $button-size: 36px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navigation-bar__button--revision {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
.navigation-bar__title--input {
|
.navigation-bar__title--input {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
<main-menu v-if="panel === 'menu'"></main-menu>
|
<main-menu v-if="panel === 'menu'"></main-menu>
|
||||||
<sync-menu v-else-if="panel === 'sync'"></sync-menu>
|
<sync-menu v-else-if="panel === 'sync'"></sync-menu>
|
||||||
<publish-menu v-else-if="panel === 'publish'"></publish-menu>
|
<publish-menu v-else-if="panel === 'publish'"></publish-menu>
|
||||||
|
<history-menu v-else-if="panel === 'history'"></history-menu>
|
||||||
<export-menu v-else-if="panel === 'export'"></export-menu>
|
<export-menu v-else-if="panel === 'export'"></export-menu>
|
||||||
<more-menu v-else-if="panel === 'more'"></more-menu>
|
<more-menu v-else-if="panel === 'more'"></more-menu>
|
||||||
<div v-else-if="panel === 'help'" class="side-bar__panel side-bar__panel--help">
|
<div v-else-if="panel === 'help'" class="side-bar__panel side-bar__panel--help">
|
||||||
@ -34,6 +35,7 @@ import Toc from './Toc';
|
|||||||
import MainMenu from './menus/MainMenu';
|
import MainMenu from './menus/MainMenu';
|
||||||
import SyncMenu from './menus/SyncMenu';
|
import SyncMenu from './menus/SyncMenu';
|
||||||
import PublishMenu from './menus/PublishMenu';
|
import PublishMenu from './menus/PublishMenu';
|
||||||
|
import HistoryMenu from './menus/HistoryMenu';
|
||||||
import ExportMenu from './menus/ExportMenu';
|
import ExportMenu from './menus/ExportMenu';
|
||||||
import MoreMenu from './menus/MoreMenu';
|
import MoreMenu from './menus/MoreMenu';
|
||||||
import markdownSample from '../data/markdownSample.md';
|
import markdownSample from '../data/markdownSample.md';
|
||||||
@ -45,6 +47,7 @@ const panelNames = {
|
|||||||
toc: 'Table of contents',
|
toc: 'Table of contents',
|
||||||
sync: 'Synchronize',
|
sync: 'Synchronize',
|
||||||
publish: 'Publish',
|
publish: 'Publish',
|
||||||
|
history: 'File history',
|
||||||
export: 'Export to disk',
|
export: 'Export to disk',
|
||||||
more: 'More',
|
more: 'More',
|
||||||
};
|
};
|
||||||
@ -55,6 +58,7 @@ export default {
|
|||||||
MainMenu,
|
MainMenu,
|
||||||
SyncMenu,
|
SyncMenu,
|
||||||
PublishMenu,
|
PublishMenu,
|
||||||
|
HistoryMenu,
|
||||||
ExportMenu,
|
ExportMenu,
|
||||||
MoreMenu,
|
MoreMenu,
|
||||||
},
|
},
|
||||||
@ -90,6 +94,7 @@ export default {
|
|||||||
hr {
|
hr {
|
||||||
margin: 10px 40px;
|
margin: 10px 40px;
|
||||||
display: none;
|
display: none;
|
||||||
|
border-top: 1px solid $hr-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
* + hr {
|
* + hr {
|
||||||
|
@ -24,6 +24,7 @@ export default {
|
|||||||
.user-image {
|
.user-image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
background-color: #fff;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
|
@ -37,20 +37,24 @@ export default class PreviewClassApplier {
|
|||||||
|
|
||||||
applyClass() {
|
applyClass() {
|
||||||
const offset = this.offsetGetter();
|
const offset = this.offsetGetter();
|
||||||
if (offset && offset.start !== offset.end) {
|
if (offset) {
|
||||||
const start = cledit.Utils.findContainer(
|
const offsetStart = editorSvc.getPreviewOffset(offset.start, editorSvc.sectionDescList);
|
||||||
editorSvc.previewElt, Math.min(offset.start, offset.end));
|
const offsetEnd = editorSvc.getPreviewOffset(offset.end, editorSvc.sectionDescList);
|
||||||
const end = cledit.Utils.findContainer(
|
if (offsetStart != null && offsetEnd != null && offsetStart !== offsetEnd) {
|
||||||
editorSvc.previewElt, Math.max(offset.start, offset.end));
|
const start = cledit.Utils.findContainer(
|
||||||
const range = document.createRange();
|
editorSvc.previewElt, Math.min(offsetStart, offsetEnd));
|
||||||
range.setStart(start.container, start.offsetInContainer);
|
const end = cledit.Utils.findContainer(
|
||||||
range.setEnd(end.container, end.offsetInContainer);
|
editorSvc.previewElt, Math.max(offsetStart, offsetEnd));
|
||||||
const properties = {
|
const range = document.createRange();
|
||||||
...this.properties,
|
range.setStart(start.container, start.offsetInContainer);
|
||||||
className: this.classGetter().join(' '),
|
range.setEnd(end.container, end.offsetInContainer);
|
||||||
};
|
const properties = {
|
||||||
utils.wrapRange(range, properties);
|
...this.properties,
|
||||||
this.lastEltCount = this.eltCollection.length;
|
className: this.classGetter().join(' '),
|
||||||
|
};
|
||||||
|
utils.wrapRange(range, properties);
|
||||||
|
this.lastEltCount = this.eltCollection.length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ textarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.textfield {
|
.textfield {
|
||||||
background-color: transparent;
|
background-color: #fff;
|
||||||
border: 0;
|
border: 0;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
@ -131,7 +131,7 @@ textarea {
|
|||||||
|
|
||||||
&[disabled] {
|
&[disabled] {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
background-color: #f8f8f8;
|
background-color: #f2f2f2;
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,6 +164,10 @@ textarea {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-name {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
.side-title {
|
.side-title {
|
||||||
height: 44px;
|
height: 44px;
|
||||||
line-height: 36px;
|
line-height: 36px;
|
||||||
@ -210,6 +214,10 @@ textarea {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
border-left: 2px solid transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.gutter__background {
|
.gutter__background {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
color: rgba(0, 0, 0, 0.75);
|
color: $body-color;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-family: $font-family-main;
|
font-family: $font-family-main;
|
||||||
font-variant-ligatures: common-ligatures;
|
font-variant-ligatures: common-ligatures;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
$font-family-main: Lato, 'Helvetica Neue', Helvetica, sans-serif;
|
$font-family-main: Lato, 'Helvetica Neue', Helvetica, sans-serif;
|
||||||
$font-family-monospace: 'Roboto Mono', 'Lucida Sans Typewriter', 'Lucida Console', monaco, Courrier, monospace;
|
$font-family-monospace: 'Roboto Mono', 'Lucida Sans Typewriter', 'Lucida Console', monaco, Courrier, monospace;
|
||||||
|
$body-color: rgba(0, 0, 0, 0.75);
|
||||||
$line-height-base: 1.67;
|
$line-height-base: 1.67;
|
||||||
$line-height-title: 1.33;
|
$line-height-title: 1.33;
|
||||||
$font-size-monospace: 0.85em;
|
$font-size-monospace: 0.85em;
|
||||||
@ -9,7 +10,7 @@ $selection-highlighting-color: #ff9632;
|
|||||||
$info-bg: transparentize($selection-highlighting-color, 0.85);
|
$info-bg: transparentize($selection-highlighting-color, 0.85);
|
||||||
$code-border-radius: 2px;
|
$code-border-radius: 2px;
|
||||||
$link-color: #0c93e4;
|
$link-color: #0c93e4;
|
||||||
$error-color: #f20;
|
$error-color: #f31;
|
||||||
$border-radius-base: 2px;
|
$border-radius-base: 2px;
|
||||||
$hr-color: rgba(128, 128, 128, 0.2);
|
$hr-color: rgba(128, 128, 128, 0.2);
|
||||||
$navbar-color: rgba(255, 255, 255, 0.67);
|
$navbar-color: rgba(255, 255, 255, 0.67);
|
||||||
|
@ -163,6 +163,14 @@ export default {
|
|||||||
() => this.updateStickyTrigger,
|
() => this.updateStickyTrigger,
|
||||||
() => this.updateSticky(),
|
() => this.updateSticky(),
|
||||||
{ immediate: true });
|
{ immediate: true });
|
||||||
|
|
||||||
|
// Move preview discussions once sectionDescWithDiffsList have been calculated
|
||||||
|
if (!editorSvc.sectionDescWithDiffsList) {
|
||||||
|
editorSvc.$once('sectionDescWithDiffsList', () => {
|
||||||
|
this.updateTops();
|
||||||
|
this.updateSticky();
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
this.scrollerElt.removeEventListener('scroll', this.updateSticky);
|
this.scrollerElt.removeEventListener('scroll', this.updateSticky);
|
||||||
@ -199,10 +207,6 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-name {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* use div selector to avoid collision with Prism */
|
/* use div selector to avoid collision with Prism */
|
||||||
div.comment {
|
div.comment {
|
||||||
padding: 5px 10px 10px;
|
padding: 5px 10px 10px;
|
||||||
|
@ -21,7 +21,11 @@ export default {
|
|||||||
clearTimeout(this.timeout);
|
clearTimeout(this.timeout);
|
||||||
this.timeout = setTimeout(() => {
|
this.timeout = setTimeout(() => {
|
||||||
let offset;
|
let offset;
|
||||||
if (editorSvc.clEditor.selectionMgr.hasFocus()) {
|
// Show the button if content is not a revision and has the focus
|
||||||
|
if (
|
||||||
|
!this.$store.state.content.revisionContent &&
|
||||||
|
editorSvc.clEditor.selectionMgr.hasFocus()
|
||||||
|
) {
|
||||||
this.selection = editorSvc.getTrimmedSelection();
|
this.selection = editorSvc.getTrimmedSelection();
|
||||||
if (this.selection) {
|
if (this.selection) {
|
||||||
const text = editorSvc.clEditor.getContent();
|
const text = editorSvc.clEditor.getContent();
|
||||||
|
@ -21,7 +21,11 @@ export default {
|
|||||||
clearTimeout(this.timeout);
|
clearTimeout(this.timeout);
|
||||||
this.timeout = setTimeout(() => {
|
this.timeout = setTimeout(() => {
|
||||||
let offset;
|
let offset;
|
||||||
if (editorSvc.previewSelectionRange) {
|
// Show the button if content is not a revision and preview selection is not empty
|
||||||
|
if (
|
||||||
|
!this.$store.state.content.revisionContent &&
|
||||||
|
editorSvc.previewSelectionRange
|
||||||
|
) {
|
||||||
this.selection = editorSvc.getTrimmedSelection();
|
this.selection = editorSvc.getTrimmedSelection();
|
||||||
if (this.selection) {
|
if (this.selection) {
|
||||||
const text = editorSvc.previewTextWithDiffsList;
|
const text = editorSvc.previewTextWithDiffsList;
|
||||||
|
260
src/components/menus/HistoryMenu.vue
Normal file
260
src/components/menus/HistoryMenu.vue
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
<template>
|
||||||
|
<div class="side-bar__panel side-bar__panel--history">
|
||||||
|
<a class="revision button flex flex--row" href="javascript:void(0)" v-for="revision in revisions" :key="revision.id" @click="open(revision)">
|
||||||
|
<div class="revision__icon">
|
||||||
|
<user-image :user-id="revision.sub"></user-image>
|
||||||
|
</div>
|
||||||
|
<div class="revision__header flex flex--column">
|
||||||
|
<user-name :user-id="revision.sub"></user-name>
|
||||||
|
<div class="revision__created">{{revision.created | formatTime}}</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<div class="flex flex--row flex--end" v-if="showMoreButton">
|
||||||
|
<button class="revision__button button" @click="showMore">More</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapMutations } from 'vuex';
|
||||||
|
import googleDriveAppDataProvider from '../../services/providers/googleDriveAppDataProvider';
|
||||||
|
import MenuEntry from './common/MenuEntry';
|
||||||
|
import UserImage from '../UserImage';
|
||||||
|
import UserName from '../UserName';
|
||||||
|
import EditorClassApplier from '../common/EditorClassApplier';
|
||||||
|
import PreviewClassApplier from '../common/PreviewClassApplier';
|
||||||
|
import utils from '../../services/utils';
|
||||||
|
import editorSvc from '../../services/editorSvc';
|
||||||
|
|
||||||
|
let editorClassAppliers = [];
|
||||||
|
let previewClassAppliers = [];
|
||||||
|
|
||||||
|
let cachedFileId;
|
||||||
|
let revisionsPromise;
|
||||||
|
let revisionContentPromises;
|
||||||
|
const pageSize = 50;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
MenuEntry,
|
||||||
|
UserImage,
|
||||||
|
UserName,
|
||||||
|
},
|
||||||
|
data: () => ({
|
||||||
|
allRevisions: [],
|
||||||
|
showCount: pageSize,
|
||||||
|
}),
|
||||||
|
computed: {
|
||||||
|
revisions() {
|
||||||
|
return this.allRevisions.slice(0, this.showCount);
|
||||||
|
},
|
||||||
|
showMoreButton() {
|
||||||
|
return this.showCount < this.allRevisions.length;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapMutations('content', [
|
||||||
|
'setRevisionContent',
|
||||||
|
]),
|
||||||
|
close() {
|
||||||
|
this.$store.dispatch('data/setSideBarPanel', 'menu');
|
||||||
|
},
|
||||||
|
showMore() {
|
||||||
|
this.showCount += pageSize;
|
||||||
|
},
|
||||||
|
open(revision) {
|
||||||
|
let revisionContentPromise = revisionContentPromises[revision.id];
|
||||||
|
if (!revisionContentPromise) {
|
||||||
|
revisionContentPromise = new Promise((resolve, reject) => {
|
||||||
|
const loginToken = this.$store.getters['data/loginToken'];
|
||||||
|
const currentFile = this.$store.getters['file/current'];
|
||||||
|
this.$store.dispatch('queue/enqueue',
|
||||||
|
() => Promise.resolve()
|
||||||
|
.then(() => googleDriveAppDataProvider.getRevisionContent(
|
||||||
|
loginToken, currentFile.id, revision.id))
|
||||||
|
.then(resolve, reject));
|
||||||
|
});
|
||||||
|
revisionContentPromises[revision.id] = revisionContentPromise;
|
||||||
|
revisionContentPromise.catch(() => {
|
||||||
|
revisionContentPromises[revision.id] = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
revisionContentPromise.then(revisionContent =>
|
||||||
|
this.$store.dispatch('content/setRevisionContent', revisionContent));
|
||||||
|
},
|
||||||
|
refreshHighlighters() {
|
||||||
|
const revisionContent = this.$store.state.content.revisionContent;
|
||||||
|
editorClassAppliers.forEach(editorClassApplier => editorClassApplier.stop());
|
||||||
|
editorClassAppliers = [];
|
||||||
|
previewClassAppliers.forEach(previewClassApplier => previewClassApplier.stop());
|
||||||
|
previewClassAppliers = [];
|
||||||
|
if (revisionContent) {
|
||||||
|
editorSvc.$once('sectionDescWithDiffsList', () => {
|
||||||
|
let offset = 0;
|
||||||
|
revisionContent.diffs.forEach(([type, text]) => {
|
||||||
|
if (type) {
|
||||||
|
const classes = ['revision-diff', `revision-diff--${type > 0 ? 'insert' : 'delete'}`];
|
||||||
|
const offsets = {
|
||||||
|
start: offset,
|
||||||
|
end: offset + text.length,
|
||||||
|
};
|
||||||
|
editorClassAppliers.push(new EditorClassApplier(
|
||||||
|
[`revision-diff--${utils.uid()}`, ...classes], offsets));
|
||||||
|
previewClassAppliers.push(new PreviewClassApplier(
|
||||||
|
[`revision-diff--${utils.uid()}`, ...classes], offsets));
|
||||||
|
}
|
||||||
|
offset += text.length;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// Watch file changes
|
||||||
|
this.$watch(
|
||||||
|
() => this.$store.getters['file/current'].id,
|
||||||
|
(id) => {
|
||||||
|
this.allRevisions = [];
|
||||||
|
if (id) {
|
||||||
|
if (id !== cachedFileId) {
|
||||||
|
this.setRevisionContent();
|
||||||
|
cachedFileId = id;
|
||||||
|
revisionContentPromises = {};
|
||||||
|
const loginToken = this.$store.getters['data/loginToken'];
|
||||||
|
const currentFile = this.$store.getters['file/current'];
|
||||||
|
revisionsPromise = new Promise((resolve, reject) => {
|
||||||
|
this.$store.dispatch('queue/enqueue',
|
||||||
|
() => Promise.resolve()
|
||||||
|
.then(() => googleDriveAppDataProvider.listRevisions(loginToken, currentFile.id))
|
||||||
|
.then((revisions) => {
|
||||||
|
resolve(revisions.sort(
|
||||||
|
(revision1, revision2) => revision2.created - revision1.created));
|
||||||
|
})
|
||||||
|
.catch(reject));
|
||||||
|
});
|
||||||
|
revisionsPromise.catch(() => {
|
||||||
|
cachedFileId = null;
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
revisionsPromise.then((revisions) => {
|
||||||
|
this.allRevisions = revisions;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
|
// Watch diffs changes
|
||||||
|
this.$watch(
|
||||||
|
() => this.$store.state.content.revisionContent,
|
||||||
|
() => this.refreshHighlighters());
|
||||||
|
|
||||||
|
// Close revision
|
||||||
|
this.onKeyup = (evt) => {
|
||||||
|
if (evt.which === 27) {
|
||||||
|
// Esc key
|
||||||
|
this.setRevisionContent();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener('keyup', this.onKeyup);
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
// Close revision
|
||||||
|
this.setRevisionContent();
|
||||||
|
// Remove highlighters
|
||||||
|
this.refreshHighlighters();
|
||||||
|
// Remove event listener
|
||||||
|
window.removeEventListener('keyup', this.onKeyup);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '../common/variables.scss';
|
||||||
|
|
||||||
|
.side-bar__panel--history {
|
||||||
|
padding: 5px 5px 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.revision {
|
||||||
|
text-align: left;
|
||||||
|
padding: 15px;
|
||||||
|
height: auto;
|
||||||
|
text-transform: none;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 24px;
|
||||||
|
border-left: 2px solid $hr-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus,
|
||||||
|
&:hover {
|
||||||
|
&::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child::before {
|
||||||
|
height: 67%;
|
||||||
|
top: 33%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.revision__icon {
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
margin-right: 12px;
|
||||||
|
flex: none;
|
||||||
|
border-radius: $border-radius-base;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.revision__header {
|
||||||
|
font-size: 15px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.revision__created {
|
||||||
|
font-size: 0.75em;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.revision__button {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout--revision {
|
||||||
|
.cledit-section *,
|
||||||
|
.cl-preview-section * {
|
||||||
|
color: rgba(0, 0, 0, 0.15) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cledit-section .revision-diff {
|
||||||
|
color: $editor-color !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl-preview-section .revision-diff {
|
||||||
|
color: $body-color !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.revision-diff {
|
||||||
|
padding: 0.25em 0;
|
||||||
|
|
||||||
|
&.revision-diff--insert {
|
||||||
|
background-color: mix(#fff, $selection-highlighting-color, 60%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.revision-diff--delete {
|
||||||
|
background-color: mix(#fff, $error-color, 60%);
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -28,6 +28,12 @@
|
|||||||
<div>File properties</div>
|
<div>File properties</div>
|
||||||
<span>Add metadata and configure extensions.</span>
|
<span>Add metadata and configure extensions.</span>
|
||||||
</menu-entry>
|
</menu-entry>
|
||||||
|
<menu-entry @click.native="history">
|
||||||
|
<icon-history slot="icon"></icon-history>
|
||||||
|
<div>File history</div>
|
||||||
|
<span>Track and restore file revisions.</span>
|
||||||
|
</menu-entry>
|
||||||
|
<hr>
|
||||||
<menu-entry @click.native="setPanel('toc')">
|
<menu-entry @click.native="setPanel('toc')">
|
||||||
<icon-toc slot="icon"></icon-toc>
|
<icon-toc slot="icon"></icon-toc>
|
||||||
Table of contents
|
Table of contents
|
||||||
@ -113,6 +119,17 @@ export default {
|
|||||||
return this.$store.dispatch('modal/open', 'fileProperties')
|
return this.$store.dispatch('modal/open', 'fileProperties')
|
||||||
.catch(() => {}); // Cancel
|
.catch(() => {}); // Cancel
|
||||||
},
|
},
|
||||||
|
history() {
|
||||||
|
const loginToken = this.$store.getters['data/loginToken'];
|
||||||
|
if (!loginToken) {
|
||||||
|
this.$store.dispatch('modal/signInForHistory')
|
||||||
|
.then(() => googleHelper.signin())
|
||||||
|
.then(() => syncSvc.requestSync())
|
||||||
|
.catch(() => { }); // Cancel
|
||||||
|
} else {
|
||||||
|
this.setPanel('history');
|
||||||
|
}
|
||||||
|
},
|
||||||
print() {
|
print() {
|
||||||
print();
|
print();
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<a href="javascript:void(0)" class="menu-entry button flex flex--row flex--align-center">
|
<a class="menu-entry button flex flex--row flex--align-center" href="javascript:void(0)">
|
||||||
<div class="menu-entry__icon flex flex--column flex--center">
|
<div class="menu-entry__icon flex flex--column flex--center">
|
||||||
<slot name="icon"></slot>
|
<slot name="icon"></slot>
|
||||||
</div>
|
</div>
|
||||||
@ -17,6 +17,7 @@
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
height: auto;
|
height: auto;
|
||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
|
line-height: 1.5;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
|
|
||||||
div div {
|
div div {
|
||||||
|
@ -53,7 +53,7 @@ export default {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 8px;
|
top: 8px;
|
||||||
right: 8px;
|
right: 8px;
|
||||||
color: rgba(0, 0, 0, 0.2);
|
color: rgba(0, 0, 0, 0.5);
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
@ -61,7 +61,7 @@ export default {
|
|||||||
&:active,
|
&:active,
|
||||||
&:focus,
|
&:focus,
|
||||||
&:hover {
|
&:hover {
|
||||||
color: rgba(0, 0, 0, 0.3);
|
color: rgba(0, 0, 0, 0.67);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
5
src/icons/History.vue
Normal file
5
src/icons/History.vue
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
|
||||||
|
<path d="M11,7V12.11L15.71,14.9L16.5,13.62L12.5,11.25V7M12.5,2C8.97,2 5.91,3.92 4.27,6.77L2,4.5V11H8.5L5.75,8.25C6.96,5.73 9.5,4 12.5,4C16.64,4 20,7.36 20,11.5C20,15.64 16.64,19 12.5,19C9.23,19 6.47,16.91 5.44,14H3.34C4.44,18.03 8.11,21 12.5,21C17.74,21 22,16.75 22,11.5C22,6.25 17.75,2 12.5,2Z" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
@ -47,6 +47,7 @@ import Undo from './Undo';
|
|||||||
import Redo from './Redo';
|
import Redo from './Redo';
|
||||||
import ContentSave from './ContentSave';
|
import ContentSave from './ContentSave';
|
||||||
import Message from './Message';
|
import Message from './Message';
|
||||||
|
import History from './History';
|
||||||
|
|
||||||
Vue.component('iconProvider', Provider);
|
Vue.component('iconProvider', Provider);
|
||||||
Vue.component('iconFormatBold', FormatBold);
|
Vue.component('iconFormatBold', FormatBold);
|
||||||
@ -96,3 +97,4 @@ Vue.component('iconUndo', Undo);
|
|||||||
Vue.component('iconRedo', Redo);
|
Vue.component('iconRedo', Redo);
|
||||||
Vue.component('iconContentSave', ContentSave);
|
Vue.component('iconContentSave', ContentSave);
|
||||||
Vue.component('iconMessage', Message);
|
Vue.component('iconMessage', Message);
|
||||||
|
Vue.component('iconHistory', History);
|
||||||
|
@ -124,9 +124,17 @@ const editorSvc = Object.assign(new Vue(), editorSvcDiscussions, editorSvcUtils,
|
|||||||
for (let i = 0; i < item[1].length; i += 1) {
|
for (let i = 0; i < item[1].length; i += 1) {
|
||||||
const section = this.conversionCtx.sectionList[sectionIdx];
|
const section = this.conversionCtx.sectionList[sectionIdx];
|
||||||
if (item[0] === 0) {
|
if (item[0] === 0) {
|
||||||
const sectionDesc = this.sectionDescList[sectionDescIdx];
|
let sectionDesc = this.sectionDescList[sectionDescIdx];
|
||||||
sectionDescIdx += 1;
|
sectionDescIdx += 1;
|
||||||
sectionDesc.editorElt = section.elt;
|
if (sectionDesc.editorElt !== section.elt) {
|
||||||
|
// Force textToPreviewDiffs computation
|
||||||
|
sectionDesc = {
|
||||||
|
...sectionDesc,
|
||||||
|
section,
|
||||||
|
editorElt: section.elt,
|
||||||
|
textToPreviewDiffs: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
newSectionDescList.push(sectionDesc);
|
newSectionDescList.push(sectionDesc);
|
||||||
previewHtml += sectionDesc.html;
|
previewHtml += sectionDesc.html;
|
||||||
sectionIdx += 1;
|
sectionIdx += 1;
|
||||||
@ -230,7 +238,8 @@ const editorSvc = Object.assign(new Vue(), editorSvcDiscussions, editorSvcUtils,
|
|||||||
}, 500),
|
}, 500),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make the diff between editor's markdown and preview's html.
|
* Compute the diffs between editor's markdown and preview's html
|
||||||
|
* asynchronously unless there is only one section to compute.
|
||||||
*/
|
*/
|
||||||
makeTextToPreviewDiffs() {
|
makeTextToPreviewDiffs() {
|
||||||
if (editorSvc.sectionDescList &&
|
if (editorSvc.sectionDescList &&
|
||||||
@ -244,7 +253,9 @@ const editorSvc = Object.assign(new Vue(), editorSvcDiscussions, editorSvcUtils,
|
|||||||
if (hasOne) {
|
if (hasOne) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
sectionDesc.previewText = sectionDesc.previewElt.textContent;
|
if (!sectionDesc.previewText) {
|
||||||
|
sectionDesc.previewText = sectionDesc.previewElt.textContent;
|
||||||
|
}
|
||||||
sectionDesc.textToPreviewDiffs = diffMatchPatch.diff_main(
|
sectionDesc.textToPreviewDiffs = diffMatchPatch.diff_main(
|
||||||
sectionDesc.section.text, sectionDesc.previewText);
|
sectionDesc.section.text, sectionDesc.previewText);
|
||||||
hasOne = true;
|
hasOne = true;
|
||||||
@ -540,7 +551,7 @@ const editorSvc = Object.assign(new Vue(), editorSvcDiscussions, editorSvcUtils,
|
|||||||
|
|
||||||
// Disable editor if hidden or if no content is loaded
|
// Disable editor if hidden or if no content is loaded
|
||||||
store.watch(
|
store.watch(
|
||||||
() => store.getters['content/current'].id && store.getters['layout/styles'].showEditor,
|
() => store.getters['content/isCurrentEditable'],
|
||||||
editable => this.clEditor.toggleEditable(!!editable), {
|
editable => this.clEditor.toggleEditable(!!editable), {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
});
|
});
|
||||||
|
@ -217,26 +217,28 @@ export default {
|
|||||||
store.watch(
|
store.watch(
|
||||||
() => store.getters['discussion/currentFileDiscussions'],
|
() => store.getters['discussion/currentFileDiscussions'],
|
||||||
(discussions) => {
|
(discussions) => {
|
||||||
|
const classGetter = (type, discussionId) => () => {
|
||||||
|
const classes = [`discussion-${type}-highlighting--${discussionId}`, `discussion-${type}-highlighting`];
|
||||||
|
if (store.state.discussion.currentDiscussionId === discussionId) {
|
||||||
|
classes.push(`discussion-${type}-highlighting--selected`);
|
||||||
|
}
|
||||||
|
return classes;
|
||||||
|
};
|
||||||
|
const offsetGetter = discussionId => () => {
|
||||||
|
const startMarker = discussionMarkers[`${discussionId}:start`];
|
||||||
|
const endMarker = discussionMarkers[`${discussionId}:end`];
|
||||||
|
return startMarker && endMarker && {
|
||||||
|
start: startMarker.offset,
|
||||||
|
end: endMarker.offset,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// Editor class appliers
|
// Editor class appliers
|
||||||
const oldEditorClassAppliers = editorClassAppliers;
|
const oldEditorClassAppliers = editorClassAppliers;
|
||||||
editorClassAppliers = {};
|
editorClassAppliers = {};
|
||||||
Object.keys(discussions).forEach((discussionId) => {
|
Object.keys(discussions).forEach((discussionId) => {
|
||||||
const classApplier = oldEditorClassAppliers[discussionId] || new EditorClassApplier(
|
const classApplier = oldEditorClassAppliers[discussionId] || new EditorClassApplier(
|
||||||
() => {
|
classGetter('editor', discussionId), offsetGetter(discussionId), { discussionId });
|
||||||
const classes = [`discussion-editor-highlighting--${discussionId}`, 'discussion-editor-highlighting'];
|
|
||||||
if (store.state.discussion.currentDiscussionId === discussionId) {
|
|
||||||
classes.push('discussion-editor-highlighting--selected');
|
|
||||||
}
|
|
||||||
return classes;
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
const startMarker = discussionMarkers[`${discussionId}:start`];
|
|
||||||
const endMarker = discussionMarkers[`${discussionId}:end`];
|
|
||||||
return startMarker && endMarker && {
|
|
||||||
start: startMarker.offset,
|
|
||||||
end: endMarker.offset,
|
|
||||||
};
|
|
||||||
}, { discussionId });
|
|
||||||
editorClassAppliers[discussionId] = classApplier;
|
editorClassAppliers[discussionId] = classApplier;
|
||||||
});
|
});
|
||||||
Object.keys(oldEditorClassAppliers).forEach((discussionId) => {
|
Object.keys(oldEditorClassAppliers).forEach((discussionId) => {
|
||||||
@ -250,21 +252,7 @@ export default {
|
|||||||
previewClassAppliers = {};
|
previewClassAppliers = {};
|
||||||
Object.keys(discussions).forEach((discussionId) => {
|
Object.keys(discussions).forEach((discussionId) => {
|
||||||
const classApplier = oldPreviewClassAppliers[discussionId] || new PreviewClassApplier(
|
const classApplier = oldPreviewClassAppliers[discussionId] || new PreviewClassApplier(
|
||||||
() => {
|
classGetter('preview', discussionId), offsetGetter(discussionId), { discussionId });
|
||||||
const classes = [`discussion-preview-highlighting--${discussionId}`, 'discussion-preview-highlighting'];
|
|
||||||
if (store.state.discussion.currentDiscussionId === discussionId) {
|
|
||||||
classes.push('discussion-preview-highlighting--selected');
|
|
||||||
}
|
|
||||||
return classes;
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
const startMarker = discussionMarkers[`${discussionId}:start`];
|
|
||||||
const endMarker = discussionMarkers[`${discussionId}:end`];
|
|
||||||
return startMarker && endMarker && {
|
|
||||||
start: this.getPreviewOffset(startMarker.offset),
|
|
||||||
end: this.getPreviewOffset(endMarker.offset),
|
|
||||||
};
|
|
||||||
}, { discussionId });
|
|
||||||
previewClassAppliers[discussionId] = classApplier;
|
previewClassAppliers[discussionId] = classApplier;
|
||||||
});
|
});
|
||||||
Object.keys(oldPreviewClassAppliers).forEach((discussionId) => {
|
Object.keys(oldPreviewClassAppliers).forEach((discussionId) => {
|
||||||
|
@ -52,12 +52,14 @@ export default {
|
|||||||
restoreScrollPosition() {
|
restoreScrollPosition() {
|
||||||
const scrollPosition = store.getters['contentState/current'].scrollPosition;
|
const scrollPosition = store.getters['contentState/current'].scrollPosition;
|
||||||
if (scrollPosition && this.sectionDescMeasuredList) {
|
if (scrollPosition && this.sectionDescMeasuredList) {
|
||||||
const objectToScroll = this.getObjectToScroll();
|
|
||||||
const sectionDesc = this.sectionDescMeasuredList[scrollPosition.sectionIdx];
|
const sectionDesc = this.sectionDescMeasuredList[scrollPosition.sectionIdx];
|
||||||
if (sectionDesc) {
|
if (sectionDesc) {
|
||||||
const scrollTop = sectionDesc[objectToScroll.dimensionKey].startOffset +
|
const editorScrollTop = sectionDesc.editorDimension.startOffset +
|
||||||
(sectionDesc[objectToScroll.dimensionKey].height * scrollPosition.posInSection);
|
(sectionDesc.editorDimension.height * scrollPosition.posInSection);
|
||||||
objectToScroll.elt.scrollTop = Math.floor(scrollTop);
|
this.editorElt.parentNode.scrollTop = Math.floor(editorScrollTop);
|
||||||
|
const previewScrollTop = sectionDesc.previewDimension.startOffset +
|
||||||
|
(sectionDesc.previewDimension.height * scrollPosition.posInSection);
|
||||||
|
this.previewElt.parentNode.scrollTop = Math.floor(previewScrollTop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -65,13 +67,17 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Get the offset in the preview corresponding to the offset of the markdown in the editor
|
* Get the offset in the preview corresponding to the offset of the markdown in the editor
|
||||||
*/
|
*/
|
||||||
getPreviewOffset(editorOffset) {
|
getPreviewOffset(editorOffset, sectionDescList = this.sectionDescWithDiffsList) {
|
||||||
if (!this.sectionDescWithDiffsList) {
|
if (!sectionDescList) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let offset = editorOffset;
|
let offset = editorOffset;
|
||||||
let previewOffset = 0;
|
let previewOffset = 0;
|
||||||
this.sectionDescWithDiffsList.some((sectionDesc) => {
|
sectionDescList.some((sectionDesc) => {
|
||||||
|
if (!sectionDesc.textToPreviewDiffs) {
|
||||||
|
previewOffset = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (sectionDesc.section.text.length >= offset) {
|
if (sectionDesc.section.text.length >= offset) {
|
||||||
previewOffset += diffMatchPatch.diff_xIndex(sectionDesc.textToPreviewDiffs, offset);
|
previewOffset += diffMatchPatch.diff_xIndex(sectionDesc.textToPreviewDiffs, offset);
|
||||||
return true;
|
return true;
|
||||||
@ -86,13 +92,17 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Get the offset of the markdown in the editor corresponding to the offset in the preview
|
* Get the offset of the markdown in the editor corresponding to the offset in the preview
|
||||||
*/
|
*/
|
||||||
getEditorOffset(previewOffset) {
|
getEditorOffset(previewOffset, sectionDescList = this.sectionDescWithDiffsList) {
|
||||||
if (!this.sectionDescWithDiffsList) {
|
if (!sectionDescList) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let offset = previewOffset;
|
let offset = previewOffset;
|
||||||
let editorOffset = 0;
|
let editorOffset = 0;
|
||||||
this.sectionDescWithDiffsList.some((sectionDesc) => {
|
sectionDescList.some((sectionDesc) => {
|
||||||
|
if (!sectionDesc.textToPreviewDiffs) {
|
||||||
|
editorOffset = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (sectionDesc.previewText.length >= offset) {
|
if (sectionDesc.previewText.length >= offset) {
|
||||||
const previewToTextDiffs = sectionDesc.textToPreviewDiffs
|
const previewToTextDiffs = sectionDesc.textToPreviewDiffs
|
||||||
.map(diff => [-diff[0], diff[1]]);
|
.map(diff => [-diff[0], diff[1]]);
|
||||||
|
@ -4,8 +4,7 @@ import editorSvc from '../../services/editorSvc';
|
|||||||
import syncSvc from '../../services/syncSvc';
|
import syncSvc from '../../services/syncSvc';
|
||||||
|
|
||||||
// Skip shortcuts if modal is open or editor is hidden
|
// Skip shortcuts if modal is open or editor is hidden
|
||||||
Mousetrap.prototype.stopCallback = () => store.getters['modal/config'] ||
|
Mousetrap.prototype.stopCallback = () => store.getters['modal/config'] || !store.getters['content/isCurrentEditable'];
|
||||||
!store.getters['content/current'].id || !store.getters['layout/styles'].showEditor;
|
|
||||||
|
|
||||||
const pagedownHandler = name => () => {
|
const pagedownHandler = name => () => {
|
||||||
editorSvc.pagedownEditor.uiManager.doClick(name);
|
editorSvc.pagedownEditor.uiManager.doClick(name);
|
||||||
|
@ -114,4 +114,18 @@ export default providerRegistry.register({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
listRevisions(token, fileId) {
|
||||||
|
const syncData = store.getters['data/syncDataByItemId'][`${fileId}/content`];
|
||||||
|
return googleHelper.getFileRevisions(token, syncData.id)
|
||||||
|
.then(revisions => revisions.map(revision => ({
|
||||||
|
id: revision.id,
|
||||||
|
sub: revision.lastModifyingUser && revision.lastModifyingUser.permissionId,
|
||||||
|
created: new Date(revision.modifiedTime).getTime(),
|
||||||
|
})));
|
||||||
|
},
|
||||||
|
getRevisionContent(token, fileId, revisionId) {
|
||||||
|
const syncData = store.getters['data/syncDataByItemId'][`${fileId}/content`];
|
||||||
|
return googleHelper.downloadFileRevision(token, syncData.id, revisionId)
|
||||||
|
.then(content => JSON.parse(content));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
@ -337,6 +337,44 @@ export default {
|
|||||||
url: `https://www.googleapis.com/drive/v3/files/${id}`,
|
url: `https://www.googleapis.com/drive/v3/files/${id}`,
|
||||||
})));
|
})));
|
||||||
},
|
},
|
||||||
|
getFileRevisions(token, id) {
|
||||||
|
return this.refreshToken(token, driveAppDataScopes)
|
||||||
|
.then((refreshedToken) => {
|
||||||
|
const revisions = [];
|
||||||
|
const getPage = pageToken => this.request(refreshedToken, {
|
||||||
|
method: 'GET',
|
||||||
|
url: `https://www.googleapis.com/drive/v3/files/${id}/revisions`,
|
||||||
|
params: {
|
||||||
|
pageToken,
|
||||||
|
pageSize: 1000,
|
||||||
|
fields: 'nextPageToken,revisions(id,modifiedTime,lastModifyingUser/permissionId,lastModifyingUser/displayName,lastModifyingUser/photoLink)',
|
||||||
|
},
|
||||||
|
}).then((res) => {
|
||||||
|
res.body.revisions.forEach((revision) => {
|
||||||
|
store.commit('userInfo/addItem', {
|
||||||
|
id: revision.lastModifyingUser.permissionId,
|
||||||
|
name: revision.lastModifyingUser.displayName,
|
||||||
|
imageUrl: revision.lastModifyingUser.photoLink,
|
||||||
|
});
|
||||||
|
revisions.push(revision);
|
||||||
|
});
|
||||||
|
if (res.body.nextPageToken) {
|
||||||
|
return getPage(res.body.nextPageToken);
|
||||||
|
}
|
||||||
|
return revisions;
|
||||||
|
});
|
||||||
|
|
||||||
|
return getPage();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
downloadFileRevision(token, fileId, revisionId) {
|
||||||
|
return this.refreshToken(token, driveAppDataScopes)
|
||||||
|
.then(refreshedToken => this.request(refreshedToken, {
|
||||||
|
method: 'GET',
|
||||||
|
url: `https://www.googleapis.com/drive/v3/files/${fileId}/revisions/${revisionId}?alt=media`,
|
||||||
|
raw: true,
|
||||||
|
}).then(res => res.body));
|
||||||
|
},
|
||||||
uploadBlogger(
|
uploadBlogger(
|
||||||
token, blogUrl, blogId, postId, title, content, labels, isDraft, published, isPage,
|
token, blogUrl, blogId, postId, title, content, labels, isDraft, published, isPage,
|
||||||
) {
|
) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import googleHelper from '../services/providers/helpers/googleHelper';
|
import googleHelper from './providers/helpers/googleHelper';
|
||||||
import store from '../store';
|
import store from '../store';
|
||||||
|
|
||||||
const promised = {};
|
const promised = {};
|
||||||
@ -19,8 +19,10 @@ export default {
|
|||||||
if (!store.state.offline) {
|
if (!store.state.offline) {
|
||||||
promised[userId] = true;
|
promised[userId] = true;
|
||||||
googleHelper.getUser(userId)
|
googleHelper.getUser(userId)
|
||||||
.catch(() => {
|
.catch((err) => {
|
||||||
promised[userId] = false;
|
if (err.status !== 404) {
|
||||||
|
promised[userId] = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,103 @@
|
|||||||
|
import DiffMatchPatch from 'diff-match-patch';
|
||||||
import moduleTemplate from './moduleTemplate';
|
import moduleTemplate from './moduleTemplate';
|
||||||
import empty from '../data/emptyContent';
|
import empty from '../data/emptyContent';
|
||||||
import utils from '../services/utils';
|
import utils from '../services/utils';
|
||||||
|
import cledit from '../libs/cledit';
|
||||||
|
|
||||||
|
const diffMatchPatch = new DiffMatchPatch();
|
||||||
|
|
||||||
const module = moduleTemplate(empty);
|
const module = moduleTemplate(empty);
|
||||||
|
|
||||||
|
module.state = {
|
||||||
|
...module.state,
|
||||||
|
revisionContent: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
module.mutations = {
|
||||||
|
...module.mutations,
|
||||||
|
setRevisionContent: (state, value) => {
|
||||||
|
if (value) {
|
||||||
|
state.revisionContent = {
|
||||||
|
...empty(),
|
||||||
|
...value,
|
||||||
|
id: utils.uid(),
|
||||||
|
hash: Date.now(),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
state.revisionContent = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
module.getters = {
|
module.getters = {
|
||||||
...module.getters,
|
...module.getters,
|
||||||
current: (state, getters, rootState, rootGetters) =>
|
current: (state, getters, rootState, rootGetters) => {
|
||||||
state.itemMap[`${rootGetters['file/current'].id}/content`] || empty(),
|
if (state.revisionContent) {
|
||||||
|
return state.revisionContent;
|
||||||
|
}
|
||||||
|
return state.itemMap[`${rootGetters['file/current'].id}/content`] || empty();
|
||||||
|
},
|
||||||
currentProperties: (state, getters) => utils.computeProperties(getters.current.properties),
|
currentProperties: (state, getters) => utils.computeProperties(getters.current.properties),
|
||||||
|
isCurrentEditable: (state, getters, rootState, rootGetters) =>
|
||||||
|
!state.revisionContent &&
|
||||||
|
getters.current.id &&
|
||||||
|
rootGetters['layout/styles'].showEditor,
|
||||||
};
|
};
|
||||||
|
|
||||||
module.actions = {
|
module.actions = {
|
||||||
...module.actions,
|
...module.actions,
|
||||||
patchCurrent({ getters, commit }, value) {
|
patchCurrent({ state, getters, commit }, value) {
|
||||||
commit('patchItem', {
|
const id = getters.current.id;
|
||||||
...value,
|
if (id && !state.revisionContent) {
|
||||||
id: getters.current.id,
|
commit('patchItem', {
|
||||||
});
|
...value,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setRevisionContent({ state, rootGetters, commit }, value) {
|
||||||
|
const currentFile = rootGetters['file/current'];
|
||||||
|
const currentContent = state.itemMap[`${currentFile.id}/content`];
|
||||||
|
if (currentContent) {
|
||||||
|
const diffs = diffMatchPatch.diff_main(currentContent.text, value.text);
|
||||||
|
diffMatchPatch.diff_cleanupSemantic(diffs);
|
||||||
|
commit('setRevisionContent', {
|
||||||
|
text: diffs.map(([, text]) => text).join(''),
|
||||||
|
diffs,
|
||||||
|
originalText: value.text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
restoreRevision({ state, getters, commit, dispatch }) {
|
||||||
|
const revisionContent = state.revisionContent;
|
||||||
|
if (revisionContent) {
|
||||||
|
dispatch('modal/fileRestoration', null, { root: true })
|
||||||
|
.then(() => {
|
||||||
|
// Close revision
|
||||||
|
commit('setRevisionContent');
|
||||||
|
const currentContent = utils.deepCopy(getters.current);
|
||||||
|
if (currentContent) {
|
||||||
|
// Restore text and move discussions
|
||||||
|
const diffs = diffMatchPatch.diff_main(
|
||||||
|
currentContent.text, revisionContent.originalText);
|
||||||
|
diffMatchPatch.diff_cleanupSemantic(diffs);
|
||||||
|
Object.keys(currentContent.discussions).forEach((discussionId) => {
|
||||||
|
const discussion = currentContent.discussions[discussionId];
|
||||||
|
const adjustOffset = (offsetName) => {
|
||||||
|
const marker = new cledit.Marker(discussion[offsetName], offsetName === 'end');
|
||||||
|
marker.adjustOffset(diffs);
|
||||||
|
discussion[offsetName] = marker.offset;
|
||||||
|
};
|
||||||
|
adjustOffset('start');
|
||||||
|
adjustOffset('end');
|
||||||
|
});
|
||||||
|
dispatch('patchCurrent', {
|
||||||
|
...currentContent,
|
||||||
|
text: revisionContent.originalText,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -70,6 +70,11 @@ export default {
|
|||||||
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',
|
resolveText: 'Ok',
|
||||||
}),
|
}),
|
||||||
|
fileRestoration: ({ dispatch }) => dispatch('open', {
|
||||||
|
content: '<p>You are about to revert some changes. Are you sure?</p>',
|
||||||
|
resolveText: 'Yes, revert',
|
||||||
|
rejectText: 'No',
|
||||||
|
}),
|
||||||
reset: ({ dispatch }) => dispatch('open', {
|
reset: ({ dispatch }) => dispatch('open', {
|
||||||
content: '<p>This will clean your local files and settings. Are you sure?</p>',
|
content: '<p>This will clean your local files and settings. Are you sure?</p>',
|
||||||
resolveText: 'Yes, clean',
|
resolveText: 'Yes, clean',
|
||||||
@ -83,13 +88,19 @@ export default {
|
|||||||
}),
|
}),
|
||||||
signInForSponsorship: ({ dispatch }) => dispatch('open', {
|
signInForSponsorship: ({ dispatch }) => dispatch('open', {
|
||||||
type: 'signInForSponsorship',
|
type: 'signInForSponsorship',
|
||||||
content: `<p>You have to sign in with <b>Google</b> to enable your sponsorship.</p>
|
content: `<p>You have to sign in with Google to enable your sponsorship.</p>
|
||||||
<div class="modal__info"><b>Note:</b> This will sync all your files and settings.</div>`,
|
<div class="modal__info"><b>Note:</b> This will sync all your files and settings.</div>`,
|
||||||
resolveText: 'Ok, sign in',
|
resolveText: 'Ok, sign in',
|
||||||
rejectText: 'Cancel',
|
rejectText: 'Cancel',
|
||||||
}),
|
}),
|
||||||
signInForComment: ({ dispatch }) => dispatch('open', {
|
signInForComment: ({ dispatch }) => dispatch('open', {
|
||||||
content: `<p>You have to sign in with <b>Google</b> to start commenting.</p>
|
content: `<p>You have to sign in with Google to start commenting.</p>
|
||||||
|
<div class="modal__info"><b>Note:</b> This will sync all your files and settings.</div>`,
|
||||||
|
resolveText: 'Ok, sign in',
|
||||||
|
rejectText: 'Cancel',
|
||||||
|
}),
|
||||||
|
signInForHistory: ({ dispatch }) => dispatch('open', {
|
||||||
|
content: `<p>You have to sign in with Google to enable revision history.</p>
|
||||||
<div class="modal__info"><b>Note:</b> This will sync all your files and settings.</div>`,
|
<div class="modal__info"><b>Note:</b> This will sync all your files and settings.</div>`,
|
||||||
resolveText: 'Ok, sign in',
|
resolveText: 'Ok, sign in',
|
||||||
rejectText: 'Cancel',
|
rejectText: 'Cancel',
|
||||||
|
@ -37,7 +37,8 @@ export default {
|
|||||||
}
|
}
|
||||||
const newQueue = queue
|
const newQueue = queue
|
||||||
.then(() => checkOffline())
|
.then(() => checkOffline())
|
||||||
.then(() => cb()
|
.then(() => Promise.resolve()
|
||||||
|
.then(() => cb())
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error(err); // eslint-disable-line no-console
|
console.error(err); // eslint-disable-line no-console
|
||||||
checkOffline();
|
checkOffline();
|
||||||
|
Loading…
Reference in New Issue
Block a user