From 6545afacd69bbb1d13e4e4c97da84004789a99e8 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Thu, 23 Feb 2023 12:30:27 +0000 Subject: [PATCH] Changed autosave handling for better editor performance This changes how the editors interact with the parent page-editor compontent, which handles auto-saving. Instead of blasting the full editor content upon any change to that parent compontent, the editors just alert of a change, without the content. The parent compontent then requests the editor content from the editor component when it needs that data for an autosave. For #3981 --- resources/js/components/markdown-editor.js | 9 ++++ resources/js/components/page-editor.js | 54 +++++++++++----------- resources/js/components/wysiwyg-editor.js | 15 +++++- resources/js/markdown/actions.js | 14 +++++- resources/js/wysiwyg/config.js | 3 +- 5 files changed, 64 insertions(+), 31 deletions(-) diff --git a/resources/js/components/markdown-editor.js b/resources/js/components/markdown-editor.js index 4c3de91f6..5cd92cae2 100644 --- a/resources/js/components/markdown-editor.js +++ b/resources/js/components/markdown-editor.js @@ -137,4 +137,13 @@ export class MarkdownEditor extends Component { return drawioAttrEl.getAttribute('drawio-url') || ''; } + /** + * Get the content of this editor. + * Used by the parent page editor component. + * @return {{html: String, markdown: String}} + */ + getContent() { + return this.editor.actions.getContent(); + } + } diff --git a/resources/js/components/page-editor.js b/resources/js/components/page-editor.js index 950a5a3b3..c58f45b66 100644 --- a/resources/js/components/page-editor.js +++ b/resources/js/components/page-editor.js @@ -33,12 +33,11 @@ export class PageEditor extends Component { 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(); @@ -59,12 +58,12 @@ export class PageEditor extends Component { 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 const updateChangelogDebounced = debounce(this.updateChangelogDisplay.bind(this), 300, false); @@ -89,18 +88,17 @@ export class PageEditor extends Component { } 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); + } - }, 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.saveDraft() } savePage() { @@ -108,14 +106,10 @@ export class PageEditor extends Component { } 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 { @@ -132,6 +126,7 @@ export class PageEditor extends Component { } didSave = true; + this.autoSave.pendingChange = false; } catch (err) { // Save the editor content in LocalStorage as a last resort, just in case. try { @@ -207,4 +202,11 @@ export class PageEditor extends Component { } } + /** + * @return MarkdownEditor|WysiwygEditor + */ + getEditorComponent() { + return window.$components.first('markdown-editor') || window.$components.first('wysiwyg-editor'); + } + } diff --git a/resources/js/components/wysiwyg-editor.js b/resources/js/components/wysiwyg-editor.js index 976dba68f..96731a0d9 100644 --- a/resources/js/components/wysiwyg-editor.js +++ b/resources/js/components/wysiwyg-editor.js @@ -25,7 +25,9 @@ export class WysiwygEditor extends Component { }); window.$events.emitPublic(this.elem, 'editor-tinymce::pre-init', {config: this.tinyMceConfig}); - window.tinymce.init(this.tinyMceConfig); + window.tinymce.init(this.tinyMceConfig).then(editors => { + this.editor = editors[0]; + }); } getDrawIoUrl() { @@ -36,4 +38,15 @@ export class WysiwygEditor extends Component { return ''; } + /** + * Get the content of this editor. + * Used by the parent page editor component. + * @return {{html: String}} + */ + getContent() { + return { + html: this.editor.getContent() + }; + } + } \ No newline at end of file diff --git a/resources/js/markdown/actions.js b/resources/js/markdown/actions.js index c2c3409a5..9faf43de3 100644 --- a/resources/js/markdown/actions.js +++ b/resources/js/markdown/actions.js @@ -6,6 +6,10 @@ export class Actions { */ constructor(editor) { this.editor = editor; + this.lastContent = { + html: '', + markdown: '', + }; } updateAndRender() { @@ -13,11 +17,17 @@ export class Actions { this.editor.config.inputEl.value = content; const html = this.editor.markdown.render(content); - window.$events.emit('editor-html-change', html); - window.$events.emit('editor-markdown-change', content); + window.$events.emit('editor-html-change', ''); + window.$events.emit('editor-markdown-change', ''); + this.lastContent.html = html; + this.lastContent.markdown = content; this.editor.display.patchWithHtml(html); } + getContent() { + return this.lastContent; + } + insertImage() { const cursorPos = this.editor.cm.getCursor('from'); /** @type {ImageManager} **/ diff --git a/resources/js/wysiwyg/config.js b/resources/js/wysiwyg/config.js index d5ec20e26..85c1919d4 100644 --- a/resources/js/wysiwyg/config.js +++ b/resources/js/wysiwyg/config.js @@ -185,11 +185,10 @@ function getSetupCallback(options) { }); function editorChange() { - const content = editor.getContent(); if (options.darkMode) { editor.contentDocument.documentElement.classList.add('dark-mode'); } - window.$events.emit('editor-html-change', content); + window.$events.emit('editor-html-change', ''); } // Custom handler hook