You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
wiki/client/components/editor.vue

304 lines
10 KiB

<template lang="pug">
v-app.editor(:dark='darkMode')
nav-header(dense)
template(slot='mid')
v-spacer
.subtitle-1.grey--text {{currentPageTitle}}
v-spacer
template(slot='actions')
v-btn.animated.fadeInDown(
text
color='green'
@click.native.stop='save'
:class='{ "is-icon": $vuetify.breakpoint.mdAndDown }'
)
v-icon(color='green', :left='$vuetify.breakpoint.lgAndUp') mdi-check
span.white--text(v-if='$vuetify.breakpoint.lgAndUp') {{ mode === 'create' ? $t('common:actions.create') : $t('common:actions.save') }}
v-btn.animated.fadeInDown.wait-p1s(
text
color='blue'
@click.native.stop='openPropsModal'
:class='{ "is-icon": $vuetify.breakpoint.mdAndDown, "mx-0": !welcomeMode, "ml-0": welcomeMode }'
)
v-icon(color='blue', :left='$vuetify.breakpoint.lgAndUp') mdi-tag-text-outline
span.white--text(v-if='$vuetify.breakpoint.lgAndUp') {{ $t('common:actions.page') }}
v-btn.animated.fadeInDown.wait-p2s(
v-if='!welcomeMode'
text
color='red'
:class='{ "is-icon": $vuetify.breakpoint.mdAndDown }'
@click.native.stop='exit'
)
v-icon(color='red', :left='$vuetify.breakpoint.lgAndUp') mdi-close
span.white--text(v-if='$vuetify.breakpoint.lgAndUp') {{ $t('common:actions.close') }}
v-content
component(:is='currentEditor', :save='save')
editor-modal-properties(v-model='dialogProps')
editor-modal-editorselect(v-model='dialogEditorSelector')
editor-modal-unsaved(v-model='dialogUnsaved', @discard='exitGo')
component(:is='activeModal')
loader(v-model='dialogProgress', :title='$t(`editor:save.processing`)', :subtitle='$t(`editor:save.pleaseWait`)')
notify
</template>
<script>
import _ from 'lodash'
import { get, sync } from 'vuex-pathify'
import { AtomSpinner } from 'epic-spinners'
import { Base64 } from 'js-base64'
import createPageMutation from 'gql/editor/create.gql'
import updatePageMutation from 'gql/editor/update.gql'
import editorStore from '@/store/editor'
/* global WIKI */
WIKI.$store.registerModule('editor', editorStore)
export default {
i18nOptions: { namespaces: 'editor' },
components: {
AtomSpinner,
editorCode: () => import(/* webpackChunkName: "editor-code", webpackMode: "lazy" */ './editor/editor-code.vue'),
editorMarkdown: () => import(/* webpackChunkName: "editor-markdown", webpackMode: "lazy" */ './editor/editor-markdown.vue'),
editorWysiwyg: () => import(/* webpackChunkName: "editor-wysiwyg", webpackMode: "lazy" */ './editor/editor-wysiwyg.vue'),
editorModalEditorselect: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-editorselect.vue'),
editorModalProperties: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-properties.vue'),
editorModalUnsaved: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-unsaved.vue'),
editorModalMedia: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-media.vue'),
editorModalBlocks: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-blocks.vue')
},
props: {
locale: {
type: String,
default: 'en'
},
path: {
type: String,
default: 'home'
},
title: {
type: String,
default: 'Untitled Page'
},
description: {
type: String,
default: ''
},
tags: {
type: Array,
default: () => ([])
},
isPublished: {
type: Boolean,
default: true
},
initEditor: {
type: String,
default: null
},
initMode: {
type: String,
default: 'create'
},
initContent: {
type: String,
default: null
},
pageId: {
type: Number,
default: 0
}
},
data() {
return {
dialogProps: false,
dialogProgress: false,
dialogEditorSelector: false,
dialogUnsaved: false,
exitConfirmed: false,
initContentParsed: ''
}
},
computed: {
currentEditor: sync('editor/editor'),
darkMode: get('site/dark'),
activeModal: sync('editor/activeModal'),
mode: get('editor/mode'),
welcomeMode() { return this.mode === `create` && this.path === `home` },
currentPageTitle: get('page/title')
},
watch: {
currentEditor(newValue, oldValue) {
if (newValue !== '' && this.mode === 'create') {
_.delay(() => {
this.dialogProps = true
}, 500)
}
}
},
created() {
this.$store.commit('page/SET_ID', this.pageId)
this.$store.commit('page/SET_DESCRIPTION', this.description)
this.$store.commit('page/SET_IS_PUBLISHED', this.isPublished)
this.$store.commit('page/SET_LOCALE', this.locale)
this.$store.commit('page/SET_PATH', this.path)
this.$store.commit('page/SET_TAGS', this.tags)
this.$store.commit('page/SET_TITLE', this.title)
this.$store.commit('page/SET_MODE', 'edit')
},
mounted() {
this.$store.set('editor/mode', this.initMode || 'create')
this.initContentParsed = this.initContent ? Base64.decode(this.initContent) : '# Header\n\nYour content here'
this.$store.set('editor/content', this.initContentParsed)
if (this.mode === 'create') {
_.delay(() => {
this.dialogEditorSelector = true
}, 500)
} else {
this.currentEditor = `editor${_.startCase(this.initEditor || 'markdown')}`
}
window.onbeforeunload = () => {
if (!this.exitConfirmed && this.initContentParsed !== this.$store.get('editor/content')) {
return 'You have unsaved edits. Are you sure you want to leave the editor?'
} else {
return undefined
}
}
},
methods: {
openPropsModal(name) {
this.dialogProps = true
},
showProgressDialog(textKey) {
this.dialogProgress = true
},
hideProgressDialog() {
this.dialogProgress = false
},
async save() {
this.showProgressDialog('saving')
try {
if (this.$store.get('editor/mode') === 'create') {
// --------------------------------------------
// -> CREATE PAGE
// --------------------------------------------
let resp = await this.$apollo.mutate({
mutation: createPageMutation,
variables: {
content: this.$store.get('editor/content'),
description: this.$store.get('page/description'),
editor: 'markdown',
locale: this.$store.get('page/locale'),
isPrivate: false,
isPublished: this.$store.get('page/isPublished'),
path: this.$store.get('page/path'),
publishEndDate: this.$store.get('page/publishEndDate') || '',
publishStartDate: this.$store.get('page/publishStartDate') || '',
tags: this.$store.get('page/tags'),
title: this.$store.get('page/title')
}
})
resp = _.get(resp, 'data.pages.create', {})
if (_.get(resp, 'responseResult.succeeded')) {
this.$store.commit('showNotification', {
message: this.$t('editor:save.createSuccess'),
style: 'success',
icon: 'check'
})
this.$store.set('editor/id', _.get(resp, 'page.id'))
this.$store.set('editor/mode', 'update')
window.location.assign(`/${this.$store.get('page/locale')}/${this.$store.get('page/path')}`)
} else {
throw new Error(_.get(resp, 'responseResult.message'))
}
} else {
// --------------------------------------------
// -> UPDATE EXISTING PAGE
// --------------------------------------------
let resp = await this.$apollo.mutate({
mutation: updatePageMutation,
variables: {
id: this.$store.get('page/id'),
content: this.$store.get('editor/content'),
description: this.$store.get('page/description'),
editor: 'markdown',
locale: this.$store.get('page/locale'),
isPrivate: false,
isPublished: this.$store.get('page/isPublished'),
path: this.$store.get('page/path'),
publishEndDate: this.$store.get('page/publishEndDate') || '',
publishStartDate: this.$store.get('page/publishStartDate') || '',
tags: this.$store.get('page/tags'),
title: this.$store.get('page/title')
}
})
resp = _.get(resp, 'data.pages.update', {})
if (_.get(resp, 'responseResult.succeeded')) {
this.$store.commit('showNotification', {
message: this.$t('editor:save.updateSuccess'),
style: 'success',
icon: 'check'
})
} else {
throw new Error(_.get(resp, 'responseResult.message'))
}
}
this.initContentParsed = this.$store.get('editor/content')
} catch (err) {
this.$store.commit('showNotification', {
message: err.message,
style: 'error',
icon: 'warning'
})
}
this.hideProgressDialog()
},
async exit() {
if (this.initContentParsed !== this.$store.get('editor/content')) {
this.dialogUnsaved = true
} else {
this.exitGo()
}
},
exitGo() {
this.$store.commit(`loadingStart`, 'editor-close')
this.currentEditor = ''
this.exitConfirmed = true
_.delay(() => {
if (this.$store.get('editor/mode') === 'create') {
window.location.assign(`/`)
} else {
window.location.assign(`/${this.$store.get('page/locale')}/${this.$store.get('page/path')}`)
}
}, 500)
}
}
}
</script>
<style lang='scss'>
.editor {
background-color: mc('grey', '900') !important;
min-height: 100vh;
.application--wrap {
background-color: mc('grey', '900');
}
}
.atom-spinner.is-inline {
display: inline-block;
}
</style>