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
This commit is contained in:
Dan Brown 2023-02-23 12:30:27 +00:00
parent 31495758a9
commit 6545afacd6
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
5 changed files with 64 additions and 31 deletions

View File

@ -137,4 +137,13 @@ export class MarkdownEditor extends Component {
return drawioAttrEl.getAttribute('drawio-url') || ''; 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();
}
} }

View File

@ -33,12 +33,11 @@ export class PageEditor extends Component {
this.setChangelogText = this.$opts.setChangelogText; this.setChangelogText = this.$opts.setChangelogText;
// State data // State data
this.editorHTML = '';
this.editorMarkdown = '';
this.autoSave = { this.autoSave = {
interval: null, interval: null,
frequency: 30000, frequency: 30000,
last: 0, last: 0,
pendingChange: false,
}; };
this.shownWarningsCache = new Set(); this.shownWarningsCache = new Set();
@ -59,12 +58,12 @@ export class PageEditor extends Component {
window.$events.listen('editor-save-page', this.savePage.bind(this)); window.$events.listen('editor-save-page', this.savePage.bind(this));
// Listen to content changes from the editor // Listen to content changes from the editor
window.$events.listen('editor-html-change', html => { const onContentChange = () => this.autoSave.pendingChange = true;
this.editorHTML = html; window.$events.listen('editor-html-change', onContentChange);
}); window.$events.listen('editor-markdown-change', onContentChange);
window.$events.listen('editor-markdown-change', markdown => {
this.editorMarkdown = markdown; // Listen to changes on the title input
}); this.titleElem.addEventListener('input', onContentChange);
// Changelog controls // Changelog controls
const updateChangelogDebounced = debounce(this.updateChangelogDisplay.bind(this), 300, false); const updateChangelogDebounced = debounce(this.updateChangelogDisplay.bind(this), 300, false);
@ -89,18 +88,17 @@ export class PageEditor extends Component {
} }
startAutoSave() { startAutoSave() {
let lastContent = this.titleElem.value.trim() + '::' + this.editorHTML; this.autoSave.interval = window.setInterval(this.runAutoSave.bind(this), this.autoSave.frequency);
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.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() { savePage() {
@ -108,14 +106,10 @@ export class PageEditor extends Component {
} }
async saveDraft() { async saveDraft() {
const data = { const data = {name: this.titleElem.value.trim()};
name: this.titleElem.value.trim(),
html: this.editorHTML,
};
if (this.editorType === 'markdown') { const editorContent = this.getEditorComponent().getContent();
data.markdown = this.editorMarkdown; Object.assign(data, editorContent);
}
let didSave = false; let didSave = false;
try { try {
@ -132,6 +126,7 @@ export class PageEditor extends Component {
} }
didSave = true; didSave = true;
this.autoSave.pendingChange = false;
} catch (err) { } catch (err) {
// Save the editor content in LocalStorage as a last resort, just in case. // Save the editor content in LocalStorage as a last resort, just in case.
try { 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');
}
} }

View File

@ -25,7 +25,9 @@ export class WysiwygEditor extends Component {
}); });
window.$events.emitPublic(this.elem, 'editor-tinymce::pre-init', {config: this.tinyMceConfig}); 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() { getDrawIoUrl() {
@ -36,4 +38,15 @@ export class WysiwygEditor extends Component {
return ''; return '';
} }
/**
* Get the content of this editor.
* Used by the parent page editor component.
* @return {{html: String}}
*/
getContent() {
return {
html: this.editor.getContent()
};
}
} }

View File

@ -6,6 +6,10 @@ export class Actions {
*/ */
constructor(editor) { constructor(editor) {
this.editor = editor; this.editor = editor;
this.lastContent = {
html: '',
markdown: '',
};
} }
updateAndRender() { updateAndRender() {
@ -13,11 +17,17 @@ export class Actions {
this.editor.config.inputEl.value = content; this.editor.config.inputEl.value = content;
const html = this.editor.markdown.render(content); const html = this.editor.markdown.render(content);
window.$events.emit('editor-html-change', html); window.$events.emit('editor-html-change', '');
window.$events.emit('editor-markdown-change', content); window.$events.emit('editor-markdown-change', '');
this.lastContent.html = html;
this.lastContent.markdown = content;
this.editor.display.patchWithHtml(html); this.editor.display.patchWithHtml(html);
} }
getContent() {
return this.lastContent;
}
insertImage() { insertImage() {
const cursorPos = this.editor.cm.getCursor('from'); const cursorPos = this.editor.cm.getCursor('from');
/** @type {ImageManager} **/ /** @type {ImageManager} **/

View File

@ -185,11 +185,10 @@ function getSetupCallback(options) {
}); });
function editorChange() { function editorChange() {
const content = editor.getContent();
if (options.darkMode) { if (options.darkMode) {
editor.contentDocument.documentElement.classList.add('dark-mode'); editor.contentDocument.documentElement.classList.add('dark-mode');
} }
window.$events.emit('editor-html-change', content); window.$events.emit('editor-html-change', '');
} }
// Custom handler hook // Custom handler hook