From 423a70d102023df9ec10c544a78809b7cb8c89d2 Mon Sep 17 00:00:00 2001 From: zadam Date: Sun, 19 Jan 2020 21:40:23 +0100 Subject: [PATCH] saving note detail --- .../javascripts/services/note_detail.js | 15 +------- .../javascripts/services/spaced_update.js | 36 ++++++++++++++++++ .../javascripts/services/tab_context.js | 1 - src/public/javascripts/services/tree.js | 1 + src/public/javascripts/widgets/note_detail.js | 37 ++++++++++++++++-- src/public/javascripts/widgets/note_title.js | 38 +------------------ .../javascripts/widgets/type_widgets/code.js | 5 ++- .../type_widgets/note_detail_search.js | 1 + .../javascripts/widgets/type_widgets/text.js | 4 +- 9 files changed, 80 insertions(+), 58 deletions(-) create mode 100644 src/public/javascripts/services/spaced_update.js diff --git a/src/public/javascripts/services/note_detail.js b/src/public/javascripts/services/note_detail.js index 72e429bae..9fa0d49cc 100644 --- a/src/public/javascripts/services/note_detail.js +++ b/src/public/javascripts/services/note_detail.js @@ -27,8 +27,6 @@ async function openInTab(notePath, activate) { } async function switchToNote(notePath) { - await saveNotesIfChanged(); - await loadNoteDetail(notePath); appContext.openTabsChanged(); @@ -38,15 +36,6 @@ function onNoteChange(func) { return appContext.getActiveTabContext().getComponent().onNoteChange(func); } -async function saveNotesIfChanged() { - for (const ctx of appContext.getTabContexts()) { - await ctx.saveNoteIfChanged(); - } - - // make sure indicator is visible in a case there was some race condition. - $savedIndicator.fadeIn(); -} - function getActiveEditor() { const activeTabContext = appContext.getActiveTabContext(); @@ -210,10 +199,9 @@ function noteChanged() { // this makes sure that when user e.g. reloads the page or navigates away from the page, the note's content is saved // this sends the request asynchronously and doesn't wait for result +// FIXME $(window).on('beforeunload', () => { saveNotesIfChanged(); }); // don't convert to short form, handler doesn't like returned promise -setInterval(saveNotesIfChanged, 3000); - export default { reload, openInTab, @@ -222,7 +210,6 @@ export default { loadNoteDetail, focusOnTitle, focusAndSelectTitle, - saveNotesIfChanged, onNoteChange, addDetailLoadedListener, getActiveEditor, diff --git a/src/public/javascripts/services/spaced_update.js b/src/public/javascripts/services/spaced_update.js new file mode 100644 index 000000000..d3686a6d7 --- /dev/null +++ b/src/public/javascripts/services/spaced_update.js @@ -0,0 +1,36 @@ +export default class SpacedUpdate { + constructor(updater, updateInterval = 1000) { + this.updater = updater; + this.lastUpdated = Date.now(); + this.changed = false; + this.updateInterval = updateInterval; + } + + scheduleUpdate() { + this.changed = true; + setTimeout(() => this.triggerUpdate()) + } + + async updateNowIfNecessary() { + if (this.changed) { + this.changed = false; + await this.updater(); + } + } + + triggerUpdate() { + if (!this.changed) { + return; + } + + if (Date.now() - this.lastUpdated > this.updateInterval) { + this.updater(); + this.lastUpdated = Date.now(); + this.changed = false; + } + else { + // update not triggered but changes are still pending so we need to schedule another check + this.scheduleUpdate(); + } + } +} diff --git a/src/public/javascripts/services/tab_context.js b/src/public/javascripts/services/tab_context.js index 980cdf672..5199f75fd 100644 --- a/src/public/javascripts/services/tab_context.js +++ b/src/public/javascripts/services/tab_context.js @@ -26,7 +26,6 @@ class TabContext extends Component { this.tabRow = tabRow; this.tabId = state.tabId || utils.randomString(4); - this.$tab = $(this.tabRow.addTab(this.tabId)); this.state = state; this.attributes = new Attributes(this.appContext, this); diff --git a/src/public/javascripts/services/tree.js b/src/public/javascripts/services/tree.js index 0c2c21a73..931a38892 100644 --- a/src/public/javascripts/services/tree.js +++ b/src/public/javascripts/services/tree.js @@ -374,6 +374,7 @@ async function createNote(node, parentNoteId, target, extraOptions = {}) { window.cutToNote.removeSelection(); } + // FIXME await noteDetailService.saveNotesIfChanged(); noteDetailService.addDetailLoadedListener(note.noteId, noteDetailService.focusAndSelectTitle); diff --git a/src/public/javascripts/widgets/note_detail.js b/src/public/javascripts/widgets/note_detail.js index 9d9c22d73..98e303a83 100644 --- a/src/public/javascripts/widgets/note_detail.js +++ b/src/public/javascripts/widgets/note_detail.js @@ -2,6 +2,8 @@ import TabAwareWidget from "./tab_aware_widget.js"; import utils from "../services/utils.js"; import protectedSessionHolder from "../services/protected_session_holder.js"; import appContext from "../services/app_context.js"; +import SpacedUpdate from "../services/spaced_update.js"; +import server from "../services/server.js"; const TPL = `
@@ -32,6 +34,19 @@ export default class NoteDetailWidget extends TabAwareWidget { this.typeWidgets = {}; this.typeWidgetPromises = {}; + + this.spacedUpdate = new SpacedUpdate(async () => { + const note = this.tabContext.note; + note.content = this.getTypeWidget().getContent(); + + const resp = await server.put('notes/' + note.noteId, note.dto); + + // FIXME: minor - does not propagate to other tab contexts with this note though + note.dateModified = resp.dateModified; + note.utcDateModified = resp.utcDateModified; + + this.trigger('noteChangesSaved', {noteId: note.noteId}) + }); } doRender() { @@ -114,12 +129,14 @@ export default class NoteDetailWidget extends TabAwareWidget { async initWidgetType(type) { const clazz = await import(typeWidgetClasses[type]); - this.typeWidgets[this.type] = new clazz.default(this.appContext); - this.children.push(this.typeWidgets[this.type]); + const typeWidget = this.typeWidgets[this.type] = new clazz.default(this.appContext); + this.children.push(typeWidget); - this.$widget.append(this.typeWidgets[this.type].render()); + this.$widget.append(typeWidget.render()); - this.typeWidgets[this.type].eventReceived('setTabContext', {tabContext: this.tabContext}); + typeWidget.onNoteChange(() => this.spacedUpdate.scheduleUpdate()); + + typeWidget.eventReceived('setTabContext', {tabContext: this.tabContext}); } getWidgetType(disableAutoBook) { @@ -153,4 +170,16 @@ export default class NoteDetailWidget extends TabAwareWidget { widget.focus(); } } + + async beforeNoteSwitchListener({tabId}) { + if (this.isTab(tabId)) { + await this.spacedUpdate.updateNowIfNecessary(); + } + } + + async beforeTabRemoveListener({tabId}) { + if (this.isTab(tabId)) { + await this.spacedUpdate.updateNowIfNecessary(); + } + } } \ No newline at end of file diff --git a/src/public/javascripts/widgets/note_title.js b/src/public/javascripts/widgets/note_title.js index 48309efbc..8ff63180f 100644 --- a/src/public/javascripts/widgets/note_title.js +++ b/src/public/javascripts/widgets/note_title.js @@ -3,6 +3,7 @@ import utils from "../services/utils.js"; import protectedSessionHolder from "../services/protected_session_holder.js"; import treeCache from "../services/tree_cache.js"; import server from "../services/server.js"; +import SpacedUpdate from "../services/spaced_update.js"; const TPL = `
@@ -24,43 +25,6 @@ const TPL = `
`; -class SpacedUpdate { - constructor(updater, updateInterval = 1000) { - this.updater = updater; - this.lastUpdated = Date.now(); - this.changed = false; - this.updateInterval = updateInterval; - } - - scheduleUpdate() { - this.changed = true; - setTimeout(() => this.triggerUpdate()) - } - - async updateNowIfNecessary() { - if (this.changed) { - this.changed = false; - await this.updater(); - } - } - - triggerUpdate() { - if (!this.changed) { - return; - } - - if (Date.now() - this.lastUpdated > this.updateInterval) { - this.updater(); - this.lastUpdated = Date.now(); - this.changed = false; - } - else { - // update not triggered but changes are still pending so we need to schedule another check - this.scheduleUpdate(); - } - } -} - export default class NoteTitleWidget extends TabAwareWidget { constructor(appContext) { super(appContext); diff --git a/src/public/javascripts/widgets/type_widgets/code.js b/src/public/javascripts/widgets/type_widgets/code.js index 072b7f381..66ab4ee4f 100644 --- a/src/public/javascripts/widgets/type_widgets/code.js +++ b/src/public/javascripts/widgets/type_widgets/code.js @@ -109,6 +109,7 @@ class CodeTypeWidget extends TypeWidget { } // make sure note is saved so we load latest changes + // FIXME await noteDetailService.saveNotesIfChanged(); if (this.tabContext.note.mime.endsWith("env=frontend")) { @@ -122,7 +123,9 @@ class CodeTypeWidget extends TypeWidget { toastService.showMessage("Note executed"); } - onNoteChange(func) { + async onNoteChange(func) { + await this.initialized; + this.codeEditor.on('change', func); } diff --git a/src/public/javascripts/widgets/type_widgets/note_detail_search.js b/src/public/javascripts/widgets/type_widgets/note_detail_search.js index 61b088c44..46e19292f 100644 --- a/src/public/javascripts/widgets/type_widgets/note_detail_search.js +++ b/src/public/javascripts/widgets/type_widgets/note_detail_search.js @@ -13,6 +13,7 @@ class NoteDetailSearch { this.$refreshButton = ctx.$tabContent.find('.note-detail-search-refresh-results-button'); this.$refreshButton.on('click', async () => { + // FIXME await noteDetailService.saveNotesIfChanged(); await searchNotesService.refreshSearch(); diff --git a/src/public/javascripts/widgets/type_widgets/text.js b/src/public/javascripts/widgets/type_widgets/text.js index 46842564f..dd66494c0 100644 --- a/src/public/javascripts/widgets/type_widgets/text.js +++ b/src/public/javascripts/widgets/type_widgets/text.js @@ -174,7 +174,9 @@ class TextTypeWidget extends TypeWidget { return this.textEditor; } - onNoteChange(func) { + async onNoteChange(func) { + await this.initialized; + this.textEditor.model.document.on('change:data', func); }