X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/85db812feaae5f36ea6214931cec4adb67a9cb39..refs/pull/4262/head:/resources/js/components/page-editor.js diff --git a/resources/js/components/page-editor.js b/resources/js/components/page-editor.js index f66e23b19..e7f4c0ba9 100644 --- a/resources/js/components/page-editor.js +++ b/resources/js/components/page-editor.js @@ -1,11 +1,10 @@ -import * as Dates from "../services/dates"; -import {onSelect} from "../services/dom"; - -/** - * Page Editor - * @extends {Component} - */ -class PageEditor { +import * as Dates from '../services/dates'; +import {onSelect} from '../services/dom'; +import {debounce} from '../services/util'; +import {Component} from './component'; + +export class PageEditor extends Component { + setup() { // Options this.draftsEnabled = this.$opts.draftsEnabled === 'true'; @@ -24,6 +23,8 @@ class PageEditor { this.draftDisplayIcon = this.$refs.draftDisplayIcon; this.changelogInput = this.$refs.changelogInput; this.changelogDisplay = this.$refs.changelogDisplay; + this.changeEditorButtons = this.$manyRefs.changeEditor || []; + this.switchDialogContainer = this.$refs.switchDialog; // Translations this.draftText = this.$opts.draftText; @@ -33,13 +34,13 @@ class PageEditor { this.setChangelogText = this.$opts.setChangelogText; // State data - this.editorHTML = ''; - this.editorMarkdown = ''; this.autoSave = { interval: null, frequency: 30000, last: 0, + pendingChange: false, }; + this.shownWarningsCache = new Set(); if (this.pageId !== 0 && this.draftsEnabled) { window.setTimeout(() => { @@ -58,24 +59,31 @@ class PageEditor { window.$events.listen('editor-save-page', this.savePage.bind(this)); // Listen to content changes from the editor - window.$events.listen('editor-html-change', html => { - this.editorHTML = html; - }); - window.$events.listen('editor-markdown-change', markdown => { - this.editorMarkdown = markdown; - }); + const onContentChange = () => { + this.autoSave.pendingChange = true; + }; + window.$events.listen('editor-html-change', onContentChange); + window.$events.listen('editor-markdown-change', onContentChange); + + // Listen to changes on the title input + this.titleElem.addEventListener('input', onContentChange); // Changelog controls - this.changelogInput.addEventListener('change', this.updateChangelogDisplay.bind(this)); + const updateChangelogDebounced = debounce(this.updateChangelogDisplay.bind(this), 300, false); + this.changelogInput.addEventListener('input', updateChangelogDebounced); // Draft Controls onSelect(this.saveDraftButton, this.saveDraft.bind(this)); onSelect(this.discardDraftButton, this.discardDraft.bind(this)); + + // Change editor controls + onSelect(this.changeEditorButtons, this.changeEditor.bind(this)); } setInitialFocus() { if (this.hasDefaultTitle) { - return this.titleElem.select(); + this.titleElem.select(); + return; } window.setTimeout(() => { @@ -84,18 +92,17 @@ class PageEditor { } startAutoSave() { - let lastContent = this.titleElem.value.trim() + '::' + this.editorHTML; - this.autoSaveInterval = window.setInterval(() => { - // Stop if manually saved recently to prevent bombarding the server - let savedRecently = (Date.now() - this.autoSave.last < (this.autoSave.frequency)/2); - if (savedRecently) return; - const newContent = this.titleElem.value.trim() + '::' + this.editorHTML; - if (newContent !== lastContent) { - lastContent = newContent; - this.saveDraft(); - } + this.autoSave.interval = window.setInterval(this.runAutoSave.bind(this), this.autoSave.frequency); + } + + runAutoSave() { + // Stop if manually saved recently to prevent bombarding the server + const savedRecently = (Date.now() - this.autoSave.last < (this.autoSave.frequency) / 2); + if (savedRecently || !this.autoSave.pendingChange) { + return; + } - }, this.autoSave.frequency); + this.saveDraft(); } savePage() { @@ -103,32 +110,40 @@ class PageEditor { } async saveDraft() { - const data = { - name: this.titleElem.value.trim(), - html: this.editorHTML, - }; + const data = {name: this.titleElem.value.trim()}; - if (this.editorType === 'markdown') { - data.markdown = this.editorMarkdown; - } + const editorContent = this.getEditorComponent().getContent(); + Object.assign(data, editorContent); + let didSave = false; try { const resp = await window.$http.put(`/ajax/page/${this.pageId}/save-draft`, data); if (!this.isNewDraft) { this.toggleDiscardDraftVisibility(true); } + this.draftNotifyChange(`${resp.data.message} ${Dates.utcTimeStampToLocalTime(resp.data.timestamp)}`); this.autoSave.last = Date.now(); + if (resp.data.warning && !this.shownWarningsCache.has(resp.data.warning)) { + window.$events.emit('warning', resp.data.warning); + this.shownWarningsCache.add(resp.data.warning); + } + + didSave = true; + this.autoSave.pendingChange = false; } catch (err) { // Save the editor content in LocalStorage as a last resort, just in case. try { const saveKey = `draft-save-fail-${(new Date()).toISOString()}`; window.localStorage.setItem(saveKey, JSON.stringify(data)); - } catch (err) {} + } catch (lsErr) { + console.error(lsErr); + } window.$events.emit('error', this.autosaveFailText); } + return didSave; } draftNotifyChange(text) { @@ -144,7 +159,8 @@ class PageEditor { try { response = await window.$http.get(`/ajax/page/${this.pageId}`); } catch (e) { - return console.error(e); + console.error(e); + return; } if (this.autoSave.interval) { @@ -153,15 +169,16 @@ class PageEditor { this.draftDisplay.innerText = this.editingPageText; this.toggleDiscardDraftVisibility(false); - window.$events.emit('editor-html-update', response.data.html || ''); - window.$events.emit('editor-markdown-update', response.data.markdown || response.data.html); + window.$events.emit('editor::replace', { + html: response.data.html, + markdown: response.data.markdown, + }); this.titleElem.value = response.data.name; window.setTimeout(() => { this.startAutoSave(); }, 1000); window.$events.emit('success', this.draftDiscardedText); - } updateChangelogDisplay() { @@ -169,7 +186,7 @@ class PageEditor { if (summary.length === 0) { summary = this.setChangelogText; } else if (summary.length > 16) { - summary = summary.slice(0, 16) + '...'; + summary = `${summary.slice(0, 16)}...`; } this.changelogDisplay.innerText = summary; } @@ -178,6 +195,24 @@ class PageEditor { this.discardDraftWrap.classList.toggle('hidden', !show); } -} + async changeEditor(event) { + event.preventDefault(); + + const link = event.target.closest('a').href; + /** @var {ConfirmDialog} * */ + const dialog = window.$components.firstOnElement(this.switchDialogContainer, 'confirm-dialog'); + const [saved, confirmed] = await Promise.all([this.saveDraft(), dialog.show()]); -export default PageEditor; \ No newline at end of file + if (saved && confirmed) { + window.location = link; + } + } + + /** + * @return MarkdownEditor|WysiwygEditor + */ + getEditorComponent() { + return window.$components.first('markdown-editor') || window.$components.first('wysiwyg-editor'); + } + +}