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.

310 lines
10 KiB

6 years ago
6 years ago
6 years ago
5 years ago
5 years ago
6 years ago
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
5 years ago
5 years ago
6 years ago
6 years ago
  1. <template lang="pug">
  2. v-app.editor(:dark='darkMode')
  3. nav-header(dense)
  4. template(slot='mid')
  5. v-spacer
  6. .subtitle-1.grey--text {{currentPageTitle}}
  7. v-spacer
  8. template(slot='actions')
  9. v-btn.animated.fadeInDown(
  10. text
  11. color='green'
  12. @click.native.stop='save'
  13. :class='{ "is-icon": $vuetify.breakpoint.mdAndDown }'
  14. )
  15. v-icon(color='green', :left='$vuetify.breakpoint.lgAndUp') mdi-check
  16. span.white--text(v-if='$vuetify.breakpoint.lgAndUp') {{ mode === 'create' ? $t('common:actions.create') : $t('common:actions.save') }}
  17. v-btn.animated.fadeInDown.wait-p1s(
  18. text
  19. color='blue'
  20. @click.native.stop='openPropsModal'
  21. :class='{ "is-icon": $vuetify.breakpoint.mdAndDown, "mx-0": !welcomeMode, "ml-0": welcomeMode }'
  22. )
  23. v-icon(color='blue', :left='$vuetify.breakpoint.lgAndUp') mdi-tag-text-outline
  24. span.white--text(v-if='$vuetify.breakpoint.lgAndUp') {{ $t('common:actions.page') }}
  25. v-btn.animated.fadeInDown.wait-p2s(
  26. v-if='!welcomeMode'
  27. text
  28. color='red'
  29. :class='{ "is-icon": $vuetify.breakpoint.mdAndDown }'
  30. @click.native.stop='exit'
  31. )
  32. v-icon(color='red', :left='$vuetify.breakpoint.lgAndUp') mdi-close
  33. span.white--text(v-if='$vuetify.breakpoint.lgAndUp') {{ $t('common:actions.close') }}
  34. v-divider.ml-3(vertical)
  35. v-content
  36. component(:is='currentEditor', :save='save')
  37. editor-modal-properties(v-model='dialogProps')
  38. editor-modal-editorselect(v-model='dialogEditorSelector')
  39. editor-modal-unsaved(v-model='dialogUnsaved', @discard='exitGo')
  40. component(:is='activeModal')
  41. loader(v-model='dialogProgress', :title='$t(`editor:save.processing`)', :subtitle='$t(`editor:save.pleaseWait`)')
  42. notify
  43. </template>
  44. <script>
  45. import _ from 'lodash'
  46. import { get, sync } from 'vuex-pathify'
  47. import { AtomSpinner } from 'epic-spinners'
  48. import { Base64 } from 'js-base64'
  49. import createPageMutation from 'gql/editor/create.gql'
  50. import updatePageMutation from 'gql/editor/update.gql'
  51. import editorStore from '../store/editor'
  52. /* global WIKI */
  53. WIKI.$store.registerModule('editor', editorStore)
  54. export default {
  55. i18nOptions: { namespaces: 'editor' },
  56. components: {
  57. AtomSpinner,
  58. editorCode: () => import(/* webpackChunkName: "editor-code", webpackMode: "lazy" */ './editor/editor-code.vue'),
  59. editorCkeditor: () => import(/* webpackChunkName: "editor-ckeditor", webpackMode: "lazy" */ './editor/editor-ckeditor.vue'),
  60. editorMarkdown: () => import(/* webpackChunkName: "editor-markdown", webpackMode: "lazy" */ './editor/editor-markdown.vue'),
  61. editorModalEditorselect: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-editorselect.vue'),
  62. editorModalProperties: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-properties.vue'),
  63. editorModalUnsaved: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-unsaved.vue'),
  64. editorModalMedia: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-media.vue'),
  65. editorModalBlocks: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-blocks.vue')
  66. },
  67. props: {
  68. locale: {
  69. type: String,
  70. default: 'en'
  71. },
  72. path: {
  73. type: String,
  74. default: 'home'
  75. },
  76. title: {
  77. type: String,
  78. default: 'Untitled Page'
  79. },
  80. description: {
  81. type: String,
  82. default: ''
  83. },
  84. tags: {
  85. type: Array,
  86. default: () => ([])
  87. },
  88. isPublished: {
  89. type: Boolean,
  90. default: true
  91. },
  92. initEditor: {
  93. type: String,
  94. default: null
  95. },
  96. initMode: {
  97. type: String,
  98. default: 'create'
  99. },
  100. initContent: {
  101. type: String,
  102. default: null
  103. },
  104. pageId: {
  105. type: Number,
  106. default: 0
  107. }
  108. },
  109. data() {
  110. return {
  111. dialogProps: false,
  112. dialogProgress: false,
  113. dialogEditorSelector: false,
  114. dialogUnsaved: false,
  115. exitConfirmed: false,
  116. initContentParsed: ''
  117. }
  118. },
  119. computed: {
  120. currentEditor: sync('editor/editor'),
  121. darkMode: get('site/dark'),
  122. activeModal: sync('editor/activeModal'),
  123. mode: get('editor/mode'),
  124. welcomeMode() { return this.mode === `create` && this.path === `home` },
  125. currentPageTitle: get('page/title')
  126. },
  127. watch: {
  128. currentEditor(newValue, oldValue) {
  129. if (newValue !== '' && this.mode === 'create') {
  130. _.delay(() => {
  131. this.dialogProps = true
  132. }, 500)
  133. }
  134. }
  135. },
  136. created() {
  137. this.$store.commit('page/SET_ID', this.pageId)
  138. this.$store.commit('page/SET_DESCRIPTION', this.description)
  139. this.$store.commit('page/SET_IS_PUBLISHED', this.isPublished)
  140. this.$store.commit('page/SET_LOCALE', this.locale)
  141. this.$store.commit('page/SET_PATH', this.path)
  142. this.$store.commit('page/SET_TAGS', this.tags)
  143. this.$store.commit('page/SET_TITLE', this.title)
  144. this.$store.commit('page/SET_MODE', 'edit')
  145. },
  146. mounted() {
  147. this.$store.set('editor/mode', this.initMode || 'create')
  148. this.initContentParsed = this.initContent ? Base64.decode(this.initContent) : ''
  149. this.$store.set('editor/content', this.initContentParsed)
  150. if (this.mode === 'create') {
  151. _.delay(() => {
  152. this.dialogEditorSelector = true
  153. }, 500)
  154. } else {
  155. this.currentEditor = `editor${_.startCase(this.initEditor || 'markdown')}`
  156. }
  157. window.onbeforeunload = () => {
  158. if (!this.exitConfirmed && this.initContentParsed !== this.$store.get('editor/content')) {
  159. return 'You have unsaved edits. Are you sure you want to leave the editor?'
  160. } else {
  161. return undefined
  162. }
  163. }
  164. },
  165. methods: {
  166. openPropsModal(name) {
  167. this.dialogProps = true
  168. },
  169. showProgressDialog(textKey) {
  170. this.dialogProgress = true
  171. },
  172. hideProgressDialog() {
  173. this.dialogProgress = false
  174. },
  175. async save() {
  176. this.showProgressDialog('saving')
  177. try {
  178. if (this.$store.get('editor/mode') === 'create') {
  179. // --------------------------------------------
  180. // -> CREATE PAGE
  181. // --------------------------------------------
  182. let resp = await this.$apollo.mutate({
  183. mutation: createPageMutation,
  184. variables: {
  185. content: this.$store.get('editor/content'),
  186. description: this.$store.get('page/description'),
  187. editor: this.$store.get('editor/editorKey'),
  188. locale: this.$store.get('page/locale'),
  189. isPrivate: false,
  190. isPublished: this.$store.get('page/isPublished'),
  191. path: this.$store.get('page/path'),
  192. publishEndDate: this.$store.get('page/publishEndDate') || '',
  193. publishStartDate: this.$store.get('page/publishStartDate') || '',
  194. tags: this.$store.get('page/tags'),
  195. title: this.$store.get('page/title')
  196. }
  197. })
  198. resp = _.get(resp, 'data.pages.create', {})
  199. if (_.get(resp, 'responseResult.succeeded')) {
  200. this.$store.commit('showNotification', {
  201. message: this.$t('editor:save.createSuccess'),
  202. style: 'success',
  203. icon: 'check'
  204. })
  205. this.$store.set('editor/id', _.get(resp, 'page.id'))
  206. this.$store.set('editor/mode', 'update')
  207. this.exitConfirmed = true
  208. window.location.assign(`/${this.$store.get('page/locale')}/${this.$store.get('page/path')}`)
  209. } else {
  210. throw new Error(_.get(resp, 'responseResult.message'))
  211. }
  212. } else {
  213. // --------------------------------------------
  214. // -> UPDATE EXISTING PAGE
  215. // --------------------------------------------
  216. let resp = await this.$apollo.mutate({
  217. mutation: updatePageMutation,
  218. variables: {
  219. id: this.$store.get('page/id'),
  220. content: this.$store.get('editor/content'),
  221. description: this.$store.get('page/description'),
  222. editor: this.$store.get('editor/editorKey'),
  223. locale: this.$store.get('page/locale'),
  224. isPrivate: false,
  225. isPublished: this.$store.get('page/isPublished'),
  226. path: this.$store.get('page/path'),
  227. publishEndDate: this.$store.get('page/publishEndDate') || '',
  228. publishStartDate: this.$store.get('page/publishStartDate') || '',
  229. tags: this.$store.get('page/tags'),
  230. title: this.$store.get('page/title')
  231. }
  232. })
  233. resp = _.get(resp, 'data.pages.update', {})
  234. if (_.get(resp, 'responseResult.succeeded')) {
  235. this.$store.commit('showNotification', {
  236. message: this.$t('editor:save.updateSuccess'),
  237. style: 'success',
  238. icon: 'check'
  239. })
  240. if (this.locale !== this.$store.get('page/locale') || this.path !== this.$store.get('page/path')) {
  241. _.delay(() => {
  242. window.location.replace(`/e/${this.$store.get('page/locale')}/${this.$store.get('page/path')}`)
  243. }, 1000)
  244. }
  245. } else {
  246. throw new Error(_.get(resp, 'responseResult.message'))
  247. }
  248. }
  249. this.initContentParsed = this.$store.get('editor/content')
  250. } catch (err) {
  251. this.$store.commit('showNotification', {
  252. message: err.message,
  253. style: 'error',
  254. icon: 'warning'
  255. })
  256. }
  257. this.hideProgressDialog()
  258. },
  259. async exit() {
  260. if (this.initContentParsed !== this.$store.get('editor/content')) {
  261. this.dialogUnsaved = true
  262. } else {
  263. this.exitGo()
  264. }
  265. },
  266. exitGo() {
  267. this.$store.commit(`loadingStart`, 'editor-close')
  268. this.currentEditor = ''
  269. this.exitConfirmed = true
  270. _.delay(() => {
  271. if (this.$store.get('editor/mode') === 'create') {
  272. window.location.assign(`/`)
  273. } else {
  274. window.location.assign(`/${this.$store.get('page/locale')}/${this.$store.get('page/path')}`)
  275. }
  276. }, 500)
  277. }
  278. }
  279. }
  280. </script>
  281. <style lang='scss'>
  282. .editor {
  283. background-color: mc('grey', '900') !important;
  284. min-height: 100vh;
  285. .application--wrap {
  286. background-color: mc('grey', '900');
  287. }
  288. }
  289. .atom-spinner.is-inline {
  290. display: inline-block;
  291. }
  292. </style>