diff --git a/Dockerfile b/Dockerfile index d6afebe95..b9efd3f0a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:12.14.0-alpine +FROM node:12.14.1-alpine # Create app directory WORKDIR /usr/src/app diff --git a/bin/build-server.sh b/bin/build-server.sh index b46121dc7..c32a59367 100755 --- a/bin/build-server.sh +++ b/bin/build-server.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash PKG_DIR=dist/trilium-linux-x64-server -NODE_VERSION=12.14.0 +NODE_VERSION=12.14.1 if [ "$1" != "DONTCOPY" ] then diff --git a/src/public/javascripts/services/app_context.js b/src/public/javascripts/services/app_context.js index f2ab435f0..ea95fd7d1 100644 --- a/src/public/javascripts/services/app_context.js +++ b/src/public/javascripts/services/app_context.js @@ -105,7 +105,7 @@ class AppContext { new TabCachingWidget(this, () => new LinkMapWidget(this)), new TabCachingWidget(this, () => new NoteRevisionsWidget(this)), new TabCachingWidget(this, () => new SimilarNotesWidget(this)), - new TabCachingWidget(this, () => new WhatLinksHereWidget(this)), + new TabCachingWidget(this, () => new WhatLinksHereWidget(this)) ]; for (const widget of rightPaneWidgets) { @@ -320,7 +320,7 @@ class AppContext { this.activeTabId = tabId; - this.trigger('activeTabChanged', { oldActiveTabId }); + this.trigger('activeTabChanged', { oldActiveTabId, newActiveTabId: tabId }); } newTabListener() { diff --git a/src/public/javascripts/services/spaced_update.js b/src/public/javascripts/services/spaced_update.js index d3686a6d7..ed67f8a2b 100644 --- a/src/public/javascripts/services/spaced_update.js +++ b/src/public/javascripts/services/spaced_update.js @@ -7,8 +7,10 @@ export default class SpacedUpdate { } scheduleUpdate() { - this.changed = true; - setTimeout(() => this.triggerUpdate()) + if (!this.changeForbidden) { + this.changed = true; + setTimeout(() => this.triggerUpdate()); + } } async updateNowIfNecessary() { @@ -33,4 +35,15 @@ export default class SpacedUpdate { this.scheduleUpdate(); } } + + allowUpdateWithoutChange(callback) { + this.changeForbidden = true; + + try { + callback(); + } + finally { + this.changeForbidden = false; + } + } } diff --git a/src/public/javascripts/widgets/component.js b/src/public/javascripts/widgets/component.js index ce813c0a4..8da089c98 100644 --- a/src/public/javascripts/widgets/component.js +++ b/src/public/javascripts/widgets/component.js @@ -11,7 +11,7 @@ export default class Component { async eventReceived(name, data, sync = false) { await this.initialized; -// console.log(`Received ${name} to ${this.componentId}`); + console.log(`Received ${name} to ${this.componentId}`); const fun = this[name + 'Listener']; diff --git a/src/public/javascripts/widgets/note_detail.js b/src/public/javascripts/widgets/note_detail.js index 0b73632e4..7d5a196e7 100644 --- a/src/public/javascripts/widgets/note_detail.js +++ b/src/public/javascripts/widgets/note_detail.js @@ -138,12 +138,11 @@ export default class NoteDetailWidget extends TabAwareWidget { const clazz = await import(typeWidgetClasses[type]); const typeWidget = this.typeWidgets[type] = new clazz.default(this.appContext); + typeWidget.spacedUpdate = this.spacedUpdate; + this.children.push(typeWidget); - this.$widget.append(typeWidget.render()); - typeWidget.onNoteChange(() => this.spacedUpdate.scheduleUpdate()); - typeWidget.eventReceived('setTabContext', {tabContext: this.tabContext}); } diff --git a/src/public/javascripts/widgets/tab_row.js b/src/public/javascripts/widgets/tab_row.js index 6970e9e0c..edb463481 100644 --- a/src/public/javascripts/widgets/tab_row.js +++ b/src/public/javascripts/widgets/tab_row.js @@ -464,8 +464,8 @@ export default class TabRowWidget extends BasicWidget { return !!this.activeTabEl; } - activeTabChangedListener({tabId}) { - const tabEl = this.getTabById(tabId)[0]; + activeTabChangedListener({newActiveTabId}) { + const tabEl = this.getTabById(newActiveTabId)[0]; const activeTabEl = this.activeTabEl; if (activeTabEl === tabEl) return; if (activeTabEl) activeTabEl.removeAttribute('active'); diff --git a/src/public/javascripts/widgets/type_widgets/book.js b/src/public/javascripts/widgets/type_widgets/book.js index 074a68cb0..aa71788bb 100644 --- a/src/public/javascripts/widgets/type_widgets/book.js +++ b/src/public/javascripts/widgets/type_widgets/book.js @@ -39,6 +39,7 @@ const TPL = ` .note-detail-book { height: 100%; padding: 10px; + position: relative; } .note-detail-book-content { @@ -280,14 +281,8 @@ export default class BookTypeWidget extends TypeWidget { return ""; } - show() { - this.$widget.show(); - } - focus() {} - onNoteChange() {} - cleanup() { this.$content.empty(); } diff --git a/src/public/javascripts/widgets/type_widgets/code.js b/src/public/javascripts/widgets/type_widgets/code.js index ffb2c2079..fced17492 100644 --- a/src/public/javascripts/widgets/type_widgets/code.js +++ b/src/public/javascripts/widgets/type_widgets/code.js @@ -68,20 +68,22 @@ export default class CodeTypeWidget extends TypeWidget { dragDrop: false // with true the editor inlines dropped files which is not what we expect }); - //this.onNoteChange(() => this.tabContext.noteChanged()); + this.codeEditor.on('change', () => this.spacedUpdate.scheduleUpdate()); } doRefresh(note) { - // CodeMirror breaks pretty badly on null so even though it shouldn't happen (guarded by consistency check) - // we provide fallback - this.codeEditor.setValue(note.content || ""); + this.spacedUpdate.allowUpdateWithoutChange(() => { + // CodeMirror breaks pretty badly on null so even though it shouldn't happen (guarded by consistency check) + // we provide fallback + this.codeEditor.setValue(note.content || ""); - const info = CodeMirror.findModeByMIME(note.mime); + const info = CodeMirror.findModeByMIME(note.mime); - if (info) { - this.codeEditor.setOption("mode", info.mime); - CodeMirror.autoLoadMode(this.codeEditor, info.mode); - } + if (info) { + this.codeEditor.setOption("mode", info.mime); + CodeMirror.autoLoadMode(this.codeEditor, info.mode); + } + }); this.show(); } @@ -123,12 +125,6 @@ export default class CodeTypeWidget extends TypeWidget { toastService.showMessage("Note executed"); } - async onNoteChange(func) { - await this.initialized; - - this.codeEditor.on('change', func); - } - cleanup() { if (this.codeEditor) { this.codeEditor.setValue(''); diff --git a/src/public/javascripts/widgets/type_widgets/empty.js b/src/public/javascripts/widgets/type_widgets/empty.js index cb3fe6229..0f401d476 100644 --- a/src/public/javascripts/widgets/type_widgets/empty.js +++ b/src/public/javascripts/widgets/type_widgets/empty.js @@ -40,14 +40,10 @@ export default class EmptyTypeWidget extends TypeWidget { this.toggle(!this.tabContext.note); } - show() {} - getContent() {} focus() {} - onNoteChange() {} - cleanup() {} scrollToTop() {} diff --git a/src/public/javascripts/widgets/type_widgets/file.js b/src/public/javascripts/widgets/type_widgets/file.js index 0b74b3f7a..d41cf3118 100644 --- a/src/public/javascripts/widgets/type_widgets/file.js +++ b/src/public/javascripts/widgets/type_widgets/file.js @@ -144,14 +144,10 @@ export default class FileTypeWidget extends TypeWidget { return utils.getUrlForDownload("api/notes/" + this.tabContext.note.noteId + "/download"); } - show() {} - getContent() {} focus() {} - onNoteChange() {} - cleanup() {} scrollToTop() {} diff --git a/src/public/javascripts/widgets/type_widgets/image.js b/src/public/javascripts/widgets/type_widgets/image.js index 829868173..865eda329 100644 --- a/src/public/javascripts/widgets/type_widgets/image.js +++ b/src/public/javascripts/widgets/type_widgets/image.js @@ -149,14 +149,10 @@ class NoteDetailImage extends TypeWidget { return utils.getUrlForDownload(`api/notes/${this.tabContext.note.noteId}/download`); } - show() {} - getContent() {} focus() {} - onNoteChange() {} - cleanup() {} scrollToTop() { diff --git a/src/public/javascripts/widgets/type_widgets/protected_session.js b/src/public/javascripts/widgets/type_widgets/protected_session.js index 195f0b9f2..f7760f893 100644 --- a/src/public/javascripts/widgets/type_widgets/protected_session.js +++ b/src/public/javascripts/widgets/type_widgets/protected_session.js @@ -40,14 +40,10 @@ export default class ProtectedSessionTypeWidget extends TypeWidget { return this.$widget; } - show() {} - getContent() {} focus() {} - onNoteChange() {} - cleanup() {} scrollToTop() { diff --git a/src/public/javascripts/widgets/type_widgets/relation_map.js b/src/public/javascripts/widgets/type_widgets/relation_map.js index ffd9362e6..be4e04720 100644 --- a/src/public/javascripts/widgets/type_widgets/relation_map.js +++ b/src/public/javascripts/widgets/type_widgets/relation_map.js @@ -644,11 +644,7 @@ export default class RelationMapTypeWidget extends TypeWidget { return JSON.stringify(this.mapData); } - show() {} - focus() {} - onNoteChange() {} - scrollToTop() {} } \ No newline at end of file diff --git a/src/public/javascripts/widgets/type_widgets/render.js b/src/public/javascripts/widgets/type_widgets/render.js index 44fad2664..d6fc86ab0 100644 --- a/src/public/javascripts/widgets/type_widgets/render.js +++ b/src/public/javascripts/widgets/type_widgets/render.js @@ -40,12 +40,8 @@ export default class RenderTypeWidget extends TypeWidget { getContent() {} - show() {} - focus() {} - onNoteChange() {} - cleanup() { this.$noteDetailRenderContent.empty(); } diff --git a/src/public/javascripts/widgets/type_widgets/search.js b/src/public/javascripts/widgets/type_widgets/search.js index d89ac418a..fdc5cb100 100644 --- a/src/public/javascripts/widgets/type_widgets/search.js +++ b/src/public/javascripts/widgets/type_widgets/search.js @@ -65,10 +65,6 @@ export default class SearchTypeWidget extends TypeWidget { focus() {} - show() {} - - onNoteChange() {} - cleanup() {} scrollToTop() {} diff --git a/src/public/javascripts/widgets/type_widgets/text.js b/src/public/javascripts/widgets/type_widgets/text.js index 189d982dc..8763cd110 100644 --- a/src/public/javascripts/widgets/type_widgets/text.js +++ b/src/public/javascripts/widgets/type_widgets/text.js @@ -128,6 +128,8 @@ export default class TextTypeWidget extends TypeWidget { } }); + this.textEditor.model.document.on('change:data', () => this.spacedUpdate.scheduleUpdate()); + if (glob.isDev && ENABLE_INSPECTOR) { await import('../../libraries/ckeditor/inspector.js'); CKEditorInspector.attach(this.textEditor); @@ -137,9 +139,9 @@ export default class TextTypeWidget extends TypeWidget { async doRefresh(note) { this.textEditor.isReadOnly = await this.isReadOnly(); - this.$widget.show(); - - this.textEditor.setData(note.content); + this.spacedUpdate.allowUpdateWithoutChange(() => { + this.textEditor.setData(note.content); + }); } getContent() { @@ -174,12 +176,6 @@ export default class TextTypeWidget extends TypeWidget { return this.textEditor; } - async onNoteChange(func) { - await this.initialized; - - this.textEditor.model.document.on('change:data', func); - } - cleanup() { if (this.textEditor) { this.textEditor.setData(''); diff --git a/src/services/ws.js b/src/services/ws.js index 5eae3f0f7..6cc4a88d2 100644 --- a/src/services/ws.js +++ b/src/services/ws.js @@ -92,7 +92,9 @@ async function fillInAdditionalProperties(sync) { } async function sendPing(client) { - const syncData = require('./sync_table').getEntitySyncsNewerThan(lastAcceptedSyncIds[client.id]); + const syncData = require('./sync_table') + .getEntitySyncsNewerThan(lastAcceptedSyncIds[client.id]) + .filter(r => r.entityName !== 'recent_notes'); // only noise ... for (const sync of syncData) { try {